JWT 로그인 개발을 수행하면서 토큰 검증을 필터단에서 수행하고 있었습니다. 토큰이 없다면 401 HttpStatus 응답코드를 반환하도록 설정했습니다. 그런데 POSTMAN으로 요청을 날려보니 500 응답코드를 반환하고 있었습니다.
왜 그런지 이유를 생각했더니 필터는 Spring Context 밖에서 동작하고 예외가 발생해도 프로젝트에서 설정해 놓은 @RestControllerAdvice
까지 가지 못하기 때문이라고 생각했습니다.
그래서 Web Context 단에서 발생하는 예외를 처리하는 과정을 기록해두려 합니다.
먼저 예외를 처리하는 필터를 생성합니다.
public class AuthFailHandlerFilter extends OncePerRequestFilter {
private final ObjectMapper objectMapper;
public AuthFailHandlerFilter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (UnAuthorizedException e) {
response.setStatus(e.getErrorCode().getStatusCode());
response.setContentType("application/json;charset=UTF-8");
response.getWriter()
.write(objectMapper.writeValueAsString(new ErrorResponse(e.getErrorCode(), e.getMessage())));
}
}
}
OncePerRequestFilter
response.setStatus
response.setContentType
application/json;charset=UTF-8
로 설정response.getWriter().write()
이제 이 필터를 빈으로 등록해주겠습니다. @Component
를 통해 빈으로 등록해도 되지만 순서 설정을 위해 다음과 같이 코드를 작성했습니다.
@Bean
public FilterRegistrationBean<JwtFilter> jwtFilter() {
FilterRegistrationBean<JwtFilter> jwtFilterBean = new FilterRegistrationBean<>();
jwtFilterBean.setFilter(new JwtFilter(jwtProvider));
jwtFilterBean.addUrlPatterns("/api/cards/*", "/api/actions/*", "/api/category/*");
jwtFilterBean.setOrder(2);
return jwtFilterBean;
}
@Bean
public FilterRegistrationBean<AuthFailHandlerFilter> AuthFailHandlerFilter() {
FilterRegistrationBean<AuthFailHandlerFilter> authFailHandlerFilterBean = new FilterRegistrationBean<>();
authFailHandlerFilterBean.setFilter(new AuthFailHandlerFilter(objectMapper));
authFailHandlerFilterBean.setOrder(1);
return authFailHandlerFilterBean;
}
이렇게 되면 아래 그림과 같이 필터의 순서가 설정됩니다. 즉 JwtFilter
에서 예외가 발생하더라도 AuthFailHandlerFilter
에서 예외를 잡을 수 있게 된 것입니다.
필터는 관리되는 영역이 다릅니다. 필터는 Spring Context 이전의 Servlet Context에서 관리되는 영역이기 때문에 필터는 스프링이 처리해주는 내용들을 적용 받을 수 없습니다. 이로 인해 스프링에 의한 예외처리가 적용되지 않습니다.
따라서 이를 처리하기 위해 예외를 처리해주는 필터를 생성해 빈으로 등록해주었습니다.
참고로
Filter
가 스프링 빈으로 등록되지 못한다고 오해할 수 있습니다. 하지만 필터는 스프링 빈으로 등록이 가능합니다.DelegatingFilterProxy
를 통해 이를 가능하게 해줍니다.
Spring boot에서는 내장 웹 서버를 지원하기 때문에 Spring Boot 가 웹 서버까지 제어가 가능해 서블릿 필터의 빈을 찾으면 서블릿 필터 체인에 필터를 등록해주게 됩니다.
아주 유익한 내용이네요!