Spring Cloud Gateway는 @ControllerAdvice를 지원하지 않아 ErrorWebExceptionHandler
를 구현해야한다.
ErrorExceptionConfig.java
@Configuration
@RequiredArgsConstructor
public class ErrorExceptionConfig {
private final ObjectMapper objectMapper;
@Bean
public ErrorWebExceptionHandler globalExceptionHandler(){
return new GlobalExceptionHandler(objectMapper);
}
}
ErrorWebExceptionHandler를 bean으로 등록하면 에러핸들러를 커스텀하여 사용할 수 있다.
GlobalExceptionHandler.java
@Slf4j
@Order(-1)
@RequiredArgsConstructor
public class GlobalExceptionHandler implements ErrorWebExceptionHandler {
private final ObjectMapper objectMapper;
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if(response.isCommitted()){
return Mono.error(ex);
}
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if(ex instanceof ResponseStatusException){
response.setStatusCode(((ResponseStatusException) ex).getStatus());
}
CustomException customException = new CustomException(ErrorCode.INVALID_ACCESS_TOKEN);
ErrorResponseDto errorResponseDto = new ErrorResponseDto(customException.getErrorCode().getMessage(), customException.getErrorCode()
.getStatus());
String error = "Gateway Error";
try {
error = objectMapper.writeValueAsString(errorResponseDto);
} catch (JsonProcessingException e) {
log.error("JsonProcessingException : " + e.getMessage());
}
byte[] bytes = error.getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
return exchange.getResponse().writeWith(Flux.just(buffer));
}
}
- GlobalExceptionHandler : 게이트웨이 내부에서 발생하는 예외에 대한 전체적인 핸들링을 담당하는 객체
- Order : 내부 bean보다 우선 순위를 높여 해당 빈이 동작하게 설정
AuthorizationHeaderFilter.java
@Slf4j
@Component
public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<AuthorizationHeaderFilter.Config> implements
Ordered {
...
@Override
public int getOrder() {
return -2;
}
@Override
public GatewayFilter apply(Config config) {
return ((exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
if(!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)){
throw new CustomException(INVALID_ACCESS_TOKEN);
}
...
return chain.filter(exchange);
});
}
...
}
필터에서 CustomException 적용
응답 예시
{
"message": "유효하지 않은 accessToken 입니다.",
"status": 401
}
에러 핸들링
GlobalExceptionHandler.java
에서 생성된 CustomException의 ErrorCode값이 고정되어 AuthorizationHeaderFilter.java
에서 어떠한 Exception을 던져도 해당 CustomException의 ErrorCode가 예외로 던져진다.
예외값이 고정되어 던져지므로 상황별로 예외를 구분하여 전달할 수 없음.
필터에서 예외처리 시 GlobalExceptionHandler.java
의 handle 메소드가 실행되므로 GlobalExceptionHandler
에서 예외처리 상황을 구분하여 처리함.(필터에 들어오는 ServerWebExchange 값이 그대로 GlobalExceptionHandler의 handle 메소드로 전달됨)
GlobalExceptionHandler.java
@Slf4j
@Order(-1)
@RequiredArgsConstructor
public class GlobalExceptionHandler implements ErrorWebExceptionHandler {
private final ObjectMapper objectMapper;
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
if(response.isCommitted()){
return Mono.error(ex);
}
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if(ex instanceof ResponseStatusException){
response.setStatusCode(((ResponseStatusException) ex).getStatus());
}
CustomException customException = new CustomException(ErrorCode.INVALID_ACCESS_TOKEN);
if(!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)){
customException = new CustomException(ErrorCode.INVALID_ACCESS);
}
ErrorResponseDto errorResponseDto = new ErrorResponseDto(customException.getErrorCode().getMessage(), customException.getErrorCode()
.getStatus());
String error = "Gateway Error";
try {
error = objectMapper.writeValueAsString(errorResponseDto);
} catch (JsonProcessingException e) {
log.error("JsonProcessingException : " + e.getMessage());
}
byte[] bytes = error.getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
return exchange.getResponse().writeWith(Flux.just(buffer));
}
}
'Spring' 카테고리의 다른 글
Spring Cloud Config URL 조회 시 보안 (0) | 2023.11.04 |
---|---|
Embedded Redis로 테스트 환경 구축하기 (0) | 2023.10.30 |
Spring Cloud Config Watch (1) | 2023.10.11 |
MSA에서 로그인 되어 있는 회원 정보 조회 (0) | 2023.09.18 |
Spring MVC DispatcherServlet 분석 (0) | 2023.07.27 |