fantasy-fans-ko / lad-be

Backend for LAD (live auction draft) service
MIT License
4 stars 0 forks source link

Filter 단에서 예외가 발생했을 때, 전역으로 예외처리를 관리할 수 있을까? #23

Closed juyohan closed 2 years ago

juyohan commented 2 years ago

1. 문제 제기

JWTOAuth2 에서 발생되는 예외처리를 전역으로 관리할 수 있지 않을까? 하는 생각을 가졌습니다.

2. 문제 해결을 위한 시도

직접 3가지의 방법으로 테스트를 시도해보았습니다.

  1. JWT 와 OAuth2 에서 제공하고 있는 Handler 들을 사용하지 않고 예외를 직접 던져, @ControllerAdivce 를 선언한 클래스에서 @ExceptionHandler 으로 예외를 잡는 방법.
  2. @ExceptionHandler 으로 해당 에러를 직접 잡는게 아닌, 프로젝트에서 구현한 BusinessException 으로 에러를 던지는 방법.
  3. Handler 와 @ControllerAdivce 둘 다 선언한 상태로 어느 예외를 먼저 잡는지 확인하는 방법.

첫 번째의 방법을 선택했을 때의 결과

@ControllerAdvice 의 어노테이션을 선언한 클래스에서 @HandlerException 어노테이션으로 예외를 지정해주었음.

@RestControllerAdvice
public class GlobalExceptionAdvice extends ResponseEntityExceptionHandler {

 @ExceptionHandler(value = MalformedJwtException.class)
    public ResponseEntity<ErrorResponse> jwtValidationException(
            HttpServletRequest request
    ) {
        return ErrorResponse.toResponseError(Exceptions.NO_DATA, request);
    }
}

유효성 검토를 하는 함수

 public boolean validateToken(
            String token,
            HttpServletRequest request
) {
     try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            return true;
     } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
            throw e;
     }
     return false;
}

결과

Request : token의 값을 이상하게 넘겨줌. Response : Json 형태의 에러 응답을 보내줘야하는데, 카카오 로그인을 하라고 응답이 내려옴. 하지만, cmd 에서는 어떤 에러가 발생이 되었는지에 대한 정보가 잘 나옴.


두 번째의 방법을 선택했을 때의 결과

@RestControllerAdvice
public class GlobalExceptionAdvice extends ResponseEntityExceptionHandler {

    @ExceptionHandler(value = BusinessException.class)
    public ResponseEntity<ErrorResponse> businessException(
            BusinessException businessException,
            HttpServletRequest request
    ) {
        return ErrorResponse.toResponseError(businessException.getExceptions(), request);
    }
}

유효성 검사하는 함수

 public boolean validateToken(
            String token,
            HttpServletRequest request
) {
     try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            return true;
     } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
            throw new BusinessException(JWT_EXPIRED_TOKEN);
     }
     return false;
}

결과

Request : token 의 값을 이상하게 넘겨줌 Response : 에러 응답이 제대로 전달이 되지 않음. 하지만, cmd 에서는 제대로 에러처리가 된 것을 확인 할 수 있었음.


세 번째의 방법을 선택했을 때의 결과

두 번째의 방법에서 WebSecurityConfigurerAdapter 클래스를 상속받은 SecurityConfig 에 JWT, OAuth2 의 대한 핸들러를 추가했습니다.

      .exceptionHandling()
      .authenticationEntryPoint(jwtAuthenticationEntryPoint)
      .accessDeniedHandler(jwtAccessDeniedHandler)
...
      .and()
      .successHandler(oAuth2SuccessHandler)
      .failureHandler(oAuth2FailureHandler)

결과

Request : token 의 값을 이상하게 넘겨줌 Response : 500 Server Error 페이지를 전송시켜줌. cmd 에서는 BusinessException 의 에러처리 1개, DispatcherServlet 의 Null 예외처리 1개, ErrorPage 에 관한 Null 예외처리 1개, 총 3개의 예외를 출력해주었습니다.


3. 응답이 원하는 형태로 내려오지 않는 이유

Filter 에서 발생된 에러를 전역으로 관리를 할 수 없는걸까? 하는 생각으로 자료들을 찾아보았습니다. image 위의 사진을 보면, Spring boot 에서 에러처리를 담당하는 HandlerExceptionResolverSpring Context 에서 처리를 하는 클래스입니다. 즉, 저 HandlerExceptionHandler 가 예외를 처리하기 위해선 DispatcherServlet 를 통해 로직을 수행하는 과정 가운데 예외가 생성이 되어야 처리할 수 있습니다. 하지만 JWT 와 OAuth2 는 Spring Context 안에 있는 것이 아닌, Filter 쪽에서 처리하는 로직입니다. 그렇기 때문에 Filter 에서 발생된 예외처리를 HandlerExceptionResolve 가 처리를 할 수 없습니다. (제일 먼저 실행되는 부분이 Filter 이기 때문이다.)

4. 그러면 방법이 아예 없는 것일까?

얕은 지식으로 찾아보니, 예외처리를 하는 필터를 하나 더 생성을 하고 그 안에서 예외처리를 하는 방법으로 하는 사람이 있었습니다. 또한 자료 를 바탕으로 따라서 하려고 했지만, 여기서 사용하는 spring-security-oauth 의 라이브러리는 현재, 지원을 중단한 상태이므로 구현이 불가합니다. 참고

5. 결론

필터에서 발생된 예외처리인 만큼, Spring Context 까지 가져오지 않고 필터에서 처리를 하는게 좋을 것 같다는 제 의견입니다. 그래야 영역을 나눠 복잡하지 않고 명확할 것이라고 생각하기 때문입니다.

혹시, 더 좋은 방법이나 생각이 있으면 코멘트 달아주시기 바랍니다!

philipjkim commented 2 years ago

@juyohan 아주 잘 정리했네요. 정확히 말씀하신 결론에 저도 동의합니다. Filter 단에서 발생한 에러는 비즈로직 내에서 발생한 에러와 다르게 우리가 따로 관리하지 않는게 관례같아요. (저도 jvm world 에 오래 있어보지 않아서 잘은 모르겠지만 주위에서 주워들은 바에 의하면 ㅋㅋ)