본문 바로가기
공부/프로그래밍

[springboot, msa] gateway 서버에 Filter에서 던져진 Exception 핸들링하기

by demonic_ 2024. 12. 7.
반응형

 

msa 에서 gateway 서버를 두면 여러 기능을 할 수 있는데, 그중 하나가 바로 인증하는 기능을 추가하는 것이다. Spring security 를 사용하면 자동으로 인증하는 부분이 들어가거나 조금 수정해서 등록할 수 있는데, 여기서는 Spring Security 를 사용하지 않는다. 그 이유는,

 

 

1. gateway 서버는 webflux로 움직이기 때문에 설정하는 부분이 다소 복잡하고

2. spring security 를 인증기능만 넣기에는 무겁다고 느껴지기 떄문이다.

 

 

내 경우는 후자가 더 컸기 때문에 인증을 코드를 통해 직접 구현하는걸 하려는데, 그러다보니 WebFilter 를 상속하여 직접 구현하는 걸 선택했다.

@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthFilter implements WebFilter {
    private final JwtManager jwtManager;


    @Value("${jwt.secret}")
    private String jwtSecret;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ... 토큰 비교하면서 잘못될 경우 Exception을 던진다

 

문제는 WebFilter 에서 상속한 클래스 내에서 token이 맞지 않을 경우 401코드와 함께 Exception 을 던지려 한다. 정확히는 UnauthorizedException 를 만들어 던진다음, 예외클래스를 비교하여 최종 리턴할때 401코드와 함께 리턴하려 한다.

 

보편적으로 @RestControllerAdvice 를 사용하겠지만 WebFilter 안에서 발생한 예외는 RestControllerAdvice쪽으로 넘어가지 못한다. 그렇기 떄문에, 다른 방법을 찾아야 하는데 바로 ErrorWebExceptionHandler 를 직접 구현하는 것이다.

 

예외가 던져지면 AbstractErrorWebExceptionHandler 에서 구현된 클래스에서 처리를 하는데, 이부분을 내가 직접 구현하는 것이다. 방법은 다음과 같다

 

@Slf4j
@Component
public class GatewayErrorWebExceptionHandler implements ErrorWebExceptionHandler {

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        log.error("Handling exception: {}", ex.getMessage(), ex);

        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);

        // HTTP 응답 설정
        if (ex instanceof UnauthorizedException) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        } else {
            exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        }

        // 예외 메시지 반환
        String errorResponse = "{\"message\": \"" + ex.getMessage() + "\"}";

        // response body 설정
        DataBuffer buffer = exchange.getResponse()
                .bufferFactory()
                .wrap(errorResponse.getBytes(StandardCharsets.UTF_8));

        return exchange.getResponse().writeWith(Mono.just(buffer));
    }
}

 

 

중간에 보면 ex instanceof UnauthorizedException 를 통해서 코드를 변경한다.

그리고 메세지의 경우 예외에 들어있는 메세지를 그대로 쓴다.

이를 통해 리턴하도록 한다.

이렇게 되면 UnauthorizedException 예외가 처리된 것이라면 401 코드와 함께 메세지가 전달된다.

 

 

여담.

만약 ErrorWebExceptionHandler 를 구현하지 않는다면 WebExceptionHandler을 구현하여 핸들링 할 수 있다. 다만 이 경우는 Order의 순위를 조절해줘야 하는데, 그렇지 않으면 DefaultErrorWebExceptionHandler보다 를 호출하여 리턴되기 때문이다.

 

@Slf4j
@Component
@Order(-2)  // DefaultErrorWebExceptionHandler보다 높은 우선순위
public class GatewayErrorWebExceptionHandler implements WebExceptionHandler {
    ...

 

다만 이렇게 할 경우 위에서 이야기한 ErrorWebExceptionHandler 를 구현하는 것이 옳은 방법이다.

 

 

끝.

 

반응형

댓글