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 를 구현하는 것이 옳은 방법이다.
끝.
'공부 > 프로그래밍' 카테고리의 다른 글
Flutter 앱 개발 시리즈: SQLite 데이터베이스 다루기 + 버전관리 (1) | 2024.11.28 |
---|---|
Flutter 앱 개발 시리즈: Riverpod과 비동기 데이터 초기화 문제 해결하기 (0) | 2024.11.27 |
[springboot 3.x, querydsl] QClass 생성 및 경로 설정 (2) | 2023.03.31 |
springboot Test 중에 403, 401 에러가 날때(spring security) (2) | 2023.03.22 |
[Linux] 젠킨스(jenkins) 포트번호 변경 (0) | 2023.03.20 |
댓글