
최근, 클라이언트에서의 원활한 오류 처리를 위해, 백엔드에서 발생한 오류를 커스텀하였다.
다시말하면, 스프링의 BasicErrorController를 거치지 않고, 우리만의 오류 메시지로 바꿔주는 중이다.
근데, security filter부분의 에러처리에서 문제가 생겼다.
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
// 유효한 자격증명을 제공하지 않고 접근하려 할때 401
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"인증된 사용자가 아닙니다.");
}
}
위 코드는, 리팩토링 이전의 코드이다.
다음과 같이 sendError를 보내서 WAS에서 error임을 인지하고, 다시 Filter, Servlet, Interceptor, BasicErrorController -> Interceptor -> View Resolver 를 거친다.
결국, 위 코드는 sendError 덕분에 WAS에서 error를 인지하고 BasicErrorController를 거칠 수 있었다.
이를 해결하기 위해서 했던 첫번째 시도는, Filter에서 Exception을 던지는 것이었다.
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
throw new UnAuthorizedException("인증된 사용자가 아닙니다.");
}
}
해당 Exception을 직접 ControllerAdvice에 지정해놓았다.
하지만, ControllerAdvice에서 지정한 오류 메시지가 출력되지 않았다.
문제를 찾아보니, Controller Advice가 처리할 수 있는 부분은 딱 서블릿 이후 부터이다.
즉, Filter에서 Exception을 던진다고 이를 해결해주지 않는다는 점이다. (sendError로 던지는거는 WAS에서 error로 인지하고 다시 보내서, basicErrorController로 감)
그래서, 직접 response를 커스터마이징하는 방법으로 이를 해결하였다.
근데 생각해보면 security filter와 관련한 에러처리는 모두 handler로 등록하였었다.
.exceptionHandling((exceptionHandling) -> exceptionHandling .accessDeniedHandler(jwtAccessDeniedHandler) .authenticationEntryPoint(jwtAuthenticationEntryPoint))access가 deny될때 jwtAccessDeniedHandler가 동작하여 sendError를 보냈었다.
만약 exception을 던질 수 있었다면 이러한 handler를 등록할 필요도 없었을 것이다.
즉, filter의 특성상 당연히 오류처리는 오류 처리를 하는 handler를 등록하는 것이 맞다고 생각한다.
@Component
@Slf4j
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
unAuthorizedExceptionHandler(response);
}
private void unAuthorizedExceptionHandler(HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
try {
String json = new ObjectMapper().writeValueAsString(new ErrorResult(HttpStatus.UNAUTHORIZED,"인증된 사용자가 아닙니다."));
response.getWriter().write(json);
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
다음처럼 직접 json을 작성하여 보내서 이를 해결할 수 있었다.