[Spring Cloud] Users Microservice - 인증과 권한

jsieon97·2023년 3월 13일
0

로그인 API

POST {ContextPath}/user-service/login

RequestBody

// JSON

{
    "email" : "jsieon@naver.com",
    "password" : "test1234"
}

로그인 정보 저장

  • 사용자 로그인 정보를 저장하기 위한 RequestLogin
// RequestLogin.java

@Data
public class RequestLogin {
    @NotNull(message = "Email cannot be null")
    @Size(min = 2, message = "Email not be less than 2 characters")
    @Email
    private String email;
    @NotNull(message = "Password cannot be null")
    @Size(min = 8, message = "Password must be equals or greater than 8 characters")
    private String password;
}

인증 필터 생성

  • Spring Security를 이용한 로그인 요청 발생 시 작업을 처리해 주는 Custom Filter클래스 생성
  • UsernamePasswordAuthenticationFilter 상속
  • attemptAuthentication() 함수 구현
// AuthenticationFilter.java

public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;
    private UserService userService;
    private Environment env;

    public AuthenticationFilter(AuthenticationManager authenticationManager,
                                UserService userService,
                                Environment env) {
        this.userService = userService;
        this.env = env;
        super.setAuthenticationManager(authenticationManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {
        try {
            RequestLogin creds = new ObjectMapper().readValue(request.getInputStream(), RequestLogin.class);

            UsernamePasswordAuthenticationToken authRequest =
                    new UsernamePasswordAuthenticationToken(
                            creds.getEmail(),
                            creds.getPassword(),
                            new ArrayList<>()
            );
            // AuthenticationManager 인증 작업 요청
            // email과 password를 spring security에 사용할 수 있는 형태로 바꾼다
            return getAuthenticationManager().authenticate(authRequest);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    ...
    
}

사용자 요청에 대해 필터를 거치도록 WebSecurity 생성

  • 사용자 요청에 대해 AuthenticationFilter를 거치도록 수정
// WebSecurity.java

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurity {

    private final UserService userService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;
    private final Environment env;

    AuthenticationManager authenticationManager;

    // spring.boot 2.7 부터는 WebSecurityConfigurerAdapter가 아닌
    // SecurityFilterChain 을 사용 합니다.
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
        authenticationManagerBuilder.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder);
        authenticationManager = authenticationManagerBuilder.build();

        //AuthenticationFilter authenticationFilter = new AuthenticationFilter();
        //authenticationFilter.setAuthenticationManager(authenticationManager);

        AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager , userService , env);

        http.csrf().disable();

        http.authorizeRequests()
                //.antMatchers("/error/**").permitAll() // public abstract java.lang.String javax.servlet.ServletRequest.getRemoteAddr() is not supported 보기 싫을때 활성화
                .antMatchers("/**")
                .hasIpAddress("127.0.0.1")
                .and()
                .authenticationManager(authenticationManager)
                .addFilter(authenticationFilter)
        ;
        http.headers().frameOptions().disable();

        return http.build();
    }

    //ex) 기존의 경우 AuthenticationManagerBuilder 를 오버라이드 하여 사용 하였지만 filterChain 안에서 호출 하여 설정 합니다.
	/*
	protected void configure(AuthenticationManagerBuilder auth) throws Exception{
		auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder);
	}
	*/


    //ex)filter를 authenticationAmanger에 주입 하던 getAuthenticationFilter역시 filterChain 내부에서 사용 합니다.
	/*
	private AuthenticationFilter getAuthenticationFilter() throws Exception {
		AuthenticationFilter authenticationFilter = new AuthenticationFilter();
		authenticationFilter.setAuthenticationManager(authenticationManager);
		return authenticationFilter;
	}
	*/

}

참고
https://www.inflearn.com/chats/789887/spring-boot-2-7-%EC%9D%B4%EC%83%81%EC%9D%84-%EC%82%AC%EC%9A%A9-%ED%95%98%EC%8B%9C%EB%8A%94-%EB%B6%84%EC%9D%98-%EA%B2%BD%EC%9A%B0 (SpringBoot 2.7이상 사용시)

UserDetailService 등록

  • 기존의 UserService.java 활용
// UserService.java

public interface UserService extends UserDetailsService {
    UserDto createUser(UserDto userDto);
    UserDto getUserByUserId(String userId);
    Iterable<UserEntity> getUserByAll();
}
// UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {

    private UserRepository userRepository;
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserEntity userEntity = userRepository.findByEmail(username);

        if (userEntity == null) {
            throw new UsernameNotFoundException(username);
        }

        return new User(userEntity.getEmail(),
                userEntity.getEncryptedPwd(),
                true,
                true,
                true,
                true,
                new ArrayList<>());
    }

API Gateway 수정

  • User Service에 대한 Routes 정보 수정
// application.yml

...

spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      default-filters:
        - name: GlobalFilter
          args:
            baseMessage: Spring Cloud Gateway GlobalFilter
            preLogger: true
            postLogger: true
      routes:
        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/user-service/login
            - Method=POST
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/user-service/(?<segment>.*), /$\{segment}
        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/user-service/users
            - Method=POST
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/user-service/(?<segment>.*), /$\{segment}
        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/user-service/**
            - Method=GET
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/user-service/(?<segment>.*), /$\{segment}
  • RewritePath?
    • Path를 재설정
    • /user-service/(?<segment>.*)/$\{segment}로 Rewrite해준다.
    • ex) /user-service/users -> /users

Postman 테스트


200 OK를 반환하는 것을 볼 수 있다.

API Gateway를 거치지 않고 직접 접근 시

Email나 Password 틀릴 시 401(인증 자격이 없습니다.) 반환

profile
개발자로써 성장하는 방법

0개의 댓글