Spring Security에서 Unauthorized, Forbidden 처리

2024. 1. 15. 21:27프로젝트/[EATceed] 몸무게 증량 어플

728x90

 

JwtAuthenticationFilter에서 토큰을 검증하는 과정에서 ExpiredJwtAuthenticationException, InvalidJwtAuthenticationException 등과 같이 AuthenticationException을 상속하여 만든 커스텀한 예외들이 터질 수 있다.

해당 예외들을 하나하나 전역적으로 예외 처리하려고 한다.


개념

 

먼저, Spring Security에는 AuthenticationException, AccessDeniedException 이 존재한다.

해당 예외들은 시큐리티 필터체인의 마지막에 속해있는 ExceptionTranslationFilter에서 처리할 수 있다.

ExceptionTranslationFilter는 필터체인에 속해져 있으므로, 예외는 일반적으로 @RestControllerAdvice + @ExceptionHander로 처리할 수 없다.

하지만, HandlerExceptionResolver를 사용하면 가능하다.

https://rasony.tistory.com/186


 

@ExceptionHanlder의 동작 원리(Feat.HandlerExceptionResolver)

이번 포스팅에서는 @ExceptionHandler의 동작 원리를 살펴보도록 하겠습니다. ExceptionHandler라는 이름에서 알 수 있듯이 예외를 핸들링하는 데 사용하는 애노테이션이다. 예외를 핸들링하려면 예외가

rasony.tistory.com

 

ExceptionTranslationFilter

 

ExceptionTranslationFilter에서는 AuthenticationException이 감지되면, 필터는 authenticationEntryPoint를 실행한다.


만약 AccessDeniedException이 감지되면, 필터는 사용자가 익명 사용자인지 여부를 결정하고, 익명 사용자인 경우 authenticationEntryPoint가 실행됩니다.

익명 사용자가 아닌 경우 AccessDeniedHandler 실행됩니다.



토큰을 검증할 때 발생할 수 있는 예외는 인증 예외이기 때문에 AuthenticationEntryPoint 인터페이스를 구현한 커스텀 JwtAuthenticationPoint를 만들어준다.

 

 

AuthenticationEntryPoint

 

public interface AuthenticationEntryPoint {

	void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
			throws IOException, ServletException;

}


@Component
public class JwtAuthenticationPoint implements AuthenticationEntryPoint {

    private final HandlerExceptionResolver resolver;

    public JwtAuthenticationPoint(@Qualifier("handlerExceptionResolver") HandlerExceptionResolver resolver) {
        this.resolver = resolver;
    }

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
            throws IOException {
        resolver.resolveException(request, response, null, authException);
    }

}

 

해당 JwtAuthenticationPoint 클래스는 handlerExceptionResolver를 주입받는다.

 

HandlerExceptionResolver는 JwtAuthenticationFilter에서 발생한 예외를 DispatcherServlet으로부터 전달 받아 처리한다.

 

HandlerExceptionResolver에는 ExceptionHandlerExceptionResolver 구현체가 있는 데, 해당 클래스에서 @ExceptionHandler에 명시된 예외를 처리할 수 있다.

 

최종적으로, 전역 핸들러에서 필터에서 발생한 예외를 핸들링할 수 있다.

RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

		...
		
	@ExceptionHandler(ExpiredJwtAuthenticationException.class)
    protected ApiResponse<?> handleExpiredJwtAuthenticationException(ExpiredJwtAuthenticationException e) {
        return ApiResponseGenerator.fail(e.getMessageCode().getCode(), e.getMessageCode().getValue(), HttpStatus.UNAUTHORIZED);
    }

    @ExceptionHandler(InvalidJwtAuthenticationException.class)
    protected ApiResponse<?> handleExpiredJwtAuthenticationException(InvalidJwtAuthenticationException e) {
        return ApiResponseGenerator.fail(e.getMessageCode().getCode(), e.getMessageCode().getValue(), HttpStatus.UNAUTHORIZED);
    }

    @ExceptionHandler(UnsupportedAuthenticationException.class)
    protected ApiResponse<?> handleExpiredJwtAuthenticationException(UnsupportedAuthenticationException e) {
        return ApiResponseGenerator.fail(e.getMessageCode().getCode(), e.getMessageCode().getValue(), HttpStatus.UNAUTHORIZED);
    }

    @ExceptionHandler(AuthenticationException.class)
    protected ApiResponse<?> handleExpiredJwtAuthenticationException(AuthenticationException e) {
        return ApiResponseGenerator.fail(e.getMessage(), HttpStatus.UNAUTHORIZED);
    }
		

		...

}

 

 

감사합니다!

728x90