[Spring security] jwt token 예외처리 관련 설정(@PreAuthorize와 config 설정의 차이)
인증 과 인가
ExceptionTranslationFilter
- 이 필터는 두 가지 예외를 처리한다.
AuthenticationException: 인증 예외 처리
- AuthenticationEntiryPoint 호출 -> 401 오류 코드 전달
AccessDeniedException: 인가 예외 처리
- AccessDeniedHandler 에서 예외 처리
AuthenticationException, AccessDniedException 모두 커스텀해서 예외처리를 하고는 한다.
내가 이글을 정리하는 이유는 @PreAuthorize만 사용했을때 필터에서 ExpiredJwtException에러가 났음에도 AuthenticationException으로 빠지지 않고 AccessDniedException 예외로 빠지는 경우가 있어서 알아보았다.
@PreAuthorize는 권한을 설정하는 경우이다.
이경우 @PreAuthorize는 예외가 발생할 경우 AccessDniedException 예외처리가 발생한다.
여기서 ExpiredJwtException에러가 발생했을 때 401을 내보내고 싶다면 커스텀하게 만들 수 있다.
@Component
public class CAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException {
// ExpiredJwtException 처리
if (accessDeniedException instanceof ExpiredJwtException) {
// ...
}
// 다른 예외 처리
else {
// ...
}
}
}
Security Config에 설정하는 경우 (.requestMatchers("/admin/**").hasRole("ADMIN"))
이 경우 FilterSecurityInterceptor의 preFilter() 메서드에서 요청을 처리한다.. preFilter() 메서드는 요청의 URL을 검사하여 해당 URL이 hasRole() 메서드의 조건을 만족하는지 확인한다.. 만약 조건을 만족하는 경우 AuthenticationManager를 사용하여 인증을 수행한다.
AuthenticationManager는 JwtTokenAuthenticationProvider를 사용하여 토큰을 인증한다. JwtTokenAuthenticationProvider는 토큰이 유효한지 확인한다.. 토큰이 만료된 경우 ExpiredJwtException을 발생시게 된다.
따라서 .requestMatchers("/admin/**").hasRole("ADMIN")을 설정하는 경우, ExpiredJwtException이 발생하면 AuthenticationException으로 분기한다.
중요 포인트는 @PreAuthorize는 권한을 설정하는 경우에는 AuthenticationManager 사용하지 않고 권한에 대한 플로우만 동작하기 때문에 ExpiredJwtException예외가 나오면 @PreAuthorize의 예외처리인 AccessDniedException 예외로 발생하고 .requestMatchers("/admin/**").hasRole("ADMIN") 설정의 경우 AuthenticationManager를 사용하기 때문에 ExpiredJwtException 예외가 발생하면 ExceptionTranslationFilte가 AuthenticationException으로 분기해주는 걸로 생각 된다.
AuthenticationException 을 커스텀해서 이용하는 경우 아래 코드처럼 이용하면 된다.
@Component
public class CAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException {
// ExpiredJwtException 처리
if (accessDeniedException instanceof ExpiredJwtException) {
throw new AuthenticationException("토큰이 만료되었습니다.");
}
// 다른 예외 처리
else {
// ...
}
}
}
혹시 글을 읽으시면서 부족한 부분이나 고쳐야될 부분이 있다면 댓글 남겨주세요!