Spring Security 기반의 애플리케이션에 아이디/패스워드 로그인 인증 후, 전달 받은 JWT를 이용해 서버 측 리소스에 접근한다고 가정해 봅시다.

이 경우, 매 요청마다 JWT를 request header에 포함해서 요청 전송을 할텐데요.

이 JWT를 JwtAuthorizationFilter가 검증할 경우, 두 가지 케이스를 테스트 해 보았습니다.

 

JwtAuthorizationFilter에서 인증이나 접근 권한 등의 예외를 try ~ catch로 잡는 경우

public class JwtVerificationFilter extends OncePerRequestFilter {
    private final JwtTokenizer jwtTokenizer;
    private final CustomAuthorityUtils authorityUtils;

    public JwtVerificationFilter(JwtTokenizer jwtTokenizer,
                                 CustomAuthorityUtils authorityUtils) {
        this.jwtTokenizer = jwtTokenizer;
        this.authorityUtils = authorityUtils;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
       // JWT 검증 시, 발생하는 예외를 try ~ catch 문으로 catch 하는 경우
       try {
            Map<String, Object> claims = verifyJws(request);
            setAuthenticationToContext(claims);
        } catch (Exception e) {
            request.setAttribute("exception", e);
        }

        filterChain.doFilter(request, response);
    }
    
    ...
    ...
}
  • SecurityFilterChain 동작이 ExceptionTranslationFilter 까지 넘어 간다.
  • ExceptionTranslationFilter에서 다시 AuthorizationFilter로 동작이 넘어간다.
  • AuthorizationDecision.isGranted()이기 때문에 AccessDeniedException이 throw된다.
  • ExceptionTranslationFilter에서 예외를 catch 한다.
  • ExceptionTranslationFilter 내부에서 AccessDeniedException을 처리하는 로직을 수행한다.
    • handleAccessDeniedException()
      • 인증 여부를 체크해서 인증이 되지 않았으면 AuthenticationEntryPoint를 이용해 예외를 처리한다.
      • 인증은 된 상태이고 권한이 적절하지 않다면  AccessDeniedHandler를 이용해 예외를 처리한다.

 

JwtAuthorizationFilter에서 인증이나 접근 권한 등의 예외를 try ~ catch로 잡지 않는 경우

public class JwtVerificationFilter extends OncePerRequestFilter {
    private final JwtTokenizer jwtTokenizer;
    private final CustomAuthorityUtils authorityUtils;

    public JwtVerificationFilter(JwtTokenizer jwtTokenizer,
                                 CustomAuthorityUtils authorityUtils) {
        this.jwtTokenizer = jwtTokenizer;
        this.authorityUtils = authorityUtils;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // JWT 검증 시, try ~ catch 문으로 catch 하지 않는 경우
        Map<String, Object> claims = verifyJws(request);

        filterChain.doFilter(request, response);
    }

    ...
    ...
}
  • 예외가 throw 되어서 톰캣 같은 서블릿 컨테이너 레벨까지 전파된다.
  • `/error` URL로 리다이렉트 된다.
  • Spring Framework의 BasicErrorController의 error() 핸들러 메서드가 `/error` 요청을 수신한다.
  • 디폴트 에러 메시지를 JSON 형태로 리턴한다.

+ Recent posts

출처: http://large.tistory.com/23 [Large]