Spring Security - JWT 인증 적용기 (2) AuthenticationHandler

    반응형

    1. Handler 구현

    MemberAuthenticationHandler 클래스에서 Success, Failure 핸들러를 각각 정적 멤버로 가지도록 했다.

     

    public class MemberAuthenticationHandler {
        @Slf4j
        public static class Success implements AuthenticationSuccessHandler {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest request,
                                                HttpServletResponse response,
                                                Authentication authentication) throws IOException {
                ... // 인증 성공 시, 로깅 또는 response로 사용자 정보 전송 등의 작업
            }
        }
    
        @Slf4j
        public static class Failure implements AuthenticationFailureHandler{
            @Override
            public void onAuthenticationFailure(HttpServletRequest request,
                                                HttpServletResponse response,
                                                AuthenticationException exception) throws IOException {
                ... // 인증 실패 시, 로깅 또는 response로 사용자 정보 전송 등의 작업
            }
        }
    }

    2. Security Configuration

    SecurityConfig에서 커스텀 필터 설정을 해주었던 CustomFilterConfigurer에 핸들러를 설정했다.

    public class CustomFilterConfigurer extends AbstractHttpConfigurer<CustomFilterConfigurer, HttpSecurity> {
        @Override
        public void configure(HttpSecurity builder) throws Exception {
            ...
            // SuccessHandler, FailureHandler 각각 구현 클래스 생성 -> DI가 아닌 new도 무방하다
            jwtAuthenticationFilter.setAuthenticationSuccessHandler(new MemberAuthenticationHandler.Success());
            jwtAuthenticationFilter.setAuthenticationFailureHandler(new MemberAuthenticationHandler.Failure());
            ...
        }
    }

    3. AuthenticationFilter

    인증 성공 시 호출되는 successfulAuthentication(...)에 핸들러를 추가했다.

    실패 시 failureHandler는 알아서 호출되므로 별도로 추가할 필요는 없다.

        @Override
        protected void successfulAuthentication(HttpServletRequest request,
                                                HttpServletResponse response,
                                                FilterChain chain,
                                                Authentication authResult) throws ServletException, IOException {
            ...
            this.getSuccessHandler().onAuthenticationSuccess(request, response, authResult);
        }

    4. 테스트

    1) 인증 성공

    나는 성공 시 httpStatus는 200(OK)로 내보내고,

    API 응답 코드(status), 메세지(message), 데이터(data)를 함께 내보낸다.

    data에는 멤버 기본 정보를 함께 내보내도록 구현했다.

    성공 시 멤버 정보와, 토큰이 함께 잘 나가는 것을 확인할 수 있었다.

    2) 인증 실패

    실패 시 httpStatus는 401(Unauthorized),

    알맞은 status와 message를 내보내도록 구현했다.

    (data에 null을 함께 보낼까 생각하고 있다)

    예상한 응답값이 잘 나갔고, 헤더에는 토큰 정보가 전혀 나가지 않는 것을 확인했다.

    3) 토큰 만료 시 (추가)

    io.jsonwebtoken.ExpiredJwtException

    이 발생했다!

    5. 힘들었던 점

    작성 중에 글이 다 날아간 점...^^ 한 번 날리고 쓰는 글은 역시 느낌이 다르다.

     

    그리고 JWT 생성 또한 핸들러에서 처리할 건지 그대로 필터에서 처리할 건지 역할 분담에 대해 고민했다.

    사실 아직까지도 논리적 근거를 가진 답을 못 내리는 이유는 필터의 역할에 대해 정확히 알지 못해서인 것 같다.

    JWT 생성은 JwtTokenizer가 담당 하지만 이 생성 시점을 어디에 둬야 할 지가 고민거리였다. (이다)

    비록 필터 클래스에서 authenticate() 한 줄로 인증을 요청한다 해도, 내부적으로 처리가 위임되며(manager, provier, service...) 많은 로직을 거쳐 사용자 인증을 하는데

    성공/실패 여부를 얻는 것 까지가 필터의 역할이고

    그 후 작업할 일은 핸들러에서 맡는게 나은 것 같다고 생각은 한다.

    뭐가 더 올바른 역할분담인지는 확신하기 힘들다😓

     

    + 욕심이 생겨 성공 시 response에 멤버 정보를 함께 담아주도록 추가 구현을 했는데

    이 때도 인증 정보의 principal을 사용한다.

    principal을 get해오면서 어? 토큰 만들 때도 가져와서 썼었는데 하는 생각이 들었다.

    이렇게 되면 토큰 생성하는 작업도 필터에서 빼내오면 좋지 않을까 싶다.

    ++ 막 구현하다 보니 리팩토링이 필요한 부분이 많이 보인다.

    우선 이건 시간상 프리 프로젝트에 적용하면서 한 번 더 익힌 뒤에 정리하면 좋지 않을까 싶다^^;;;;;;

    반응형

    댓글