ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Webflux + Jwt(es256)
    개발 기록 2023. 3. 14. 02:34
    728x90

    웹플럭스 기반의 jwt인증 서버를 구현하며 문제가 됐던 부분 위주의 정리이고, 개인적인 의견이다.

    코드는 github에 있다.( https://github.com/neunggu/auth )

     

    GitHub - neunggu/auth: auth server

    auth server. Contribute to neunggu/auth development by creating an account on GitHub.

    github.com

     

    1. ReactiveAuthenticationManager를 꼭 구현해야하는가?

      ** 결론부터 이야기하면 jwt를 구현하는데 있어서 굳이 따 구현할 필요는 없다.

    먼저 AbstractUserDetailsReactiveAuthenticationManager에서 authenicate를 구현한 코드를 보면 db에서 유저 정보를 가져와 암호화된 비밀번호를 비교하는 코드이다. jwt로 인증할 예정인데 계속해서 db에서 유저정보를 가지고올 필요는 없어보인다. 굳이 사용하겠다면 사용자가 처음 로그인시 사용할 수는 있겠지만 배보다 배꼽이 커보인다.

    class AbstractUserDetailsReactiveAuthenticationManager implements ReactiveAuthenticationManager
    
    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
       String username = authentication.getName();
       String presentedPassword = (String) authentication.getCredentials();
       // @formatter:off
       return retrieveUser(username)
             .doOnNext(this.preAuthenticationChecks::check)
             .publishOn(this.scheduler)
             .filter((userDetails) -> this.passwordEncoder.matches(presentedPassword, userDetails.getPassword()))
             .switchIfEmpty(Mono.defer(() -> Mono.error(new BadCredentialsException("Invalid Credentials"))))
             .flatMap((userDetails) -> upgradeEncodingIfNecessary(userDetails, presentedPassword))
             .doOnNext(this.postAuthenticationChecks::check)
             .map(this::createUsernamePasswordAuthenticationToken);
       // @formatter:on
    }
    
    protected abstract Mono<UserDetails> retrieveUser(String username);

    두번째 이유는 SecurityWebFilterChain의 설정에 있다.

    jwt는 bearer 방식을 사용하기 때문에 httpBasic은 disable 시킨다. 그러면 AuthenticationWebFilter가 활성화되지 않고 ReactiveAuthenticationManager는 등록을 해도 사용되어지지 않는다.

    (AuthorizationWebFilter, ReactiveAuthorizationManager 라는 것이 있다. 영문 스펠이 조금 다르니 주의)

    class ServerHttpSecurity
    
    /**
     * Configures HTTP Basic authentication. An example configuration is provided below:
     *
     * <pre class="code">
     *  &#064;Bean
     *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
     *      http
     *          // ...
     *          .httpBasic()
     *              // used for authenticating the credentials
     *              .authenticationManager(authenticationManager)
     *              // Custom persistence of the authentication
     *              .securityContextRepository(securityContextRepository);
     *      return http.build();
     *  }
     * </pre>
     * @return the {@link HttpBasicSpec} to customize
     */
    public HttpBasicSpec httpBasic() {
       if (this.httpBasic == null) {
          this.httpBasic = new HttpBasicSpec();
       }
       return this.httpBasic;
    }

     

    2. stateless 설정

      SecurityWebFilterChain에서 securityContextRepository(NoOpServerSecurityContextRepository.getInstance())를 해주면 된다.

    The security context in a WebFlux application is stored in a ServerSecurityContextRepository.     Its WebSessionServerSecurityContextRepository implementation, which is used by default,     stores the context in session. Configuring a NoOpServerSecurityContextRepository     instead would make our application stateless

    3. filter 추가

      jwt 인증을 할 Webfilter를 새로 만든다. Webfilter에 @Component를 달아두면 SecurityWebFilterChain에 필터를 추가할 필요없이 자동 등록된다.

     

    ----------

    참고

    https://sthwin.tistory.com/24

    https://kdevkr.github.io/jwt/

    https://ard333.medium.com/authentication-and-authorization-using-jwt-on-spring-webflux-29b81f813e78

     

    728x90
    반응형

    댓글

Designed by Tistory.