[Spring] Filter Exception Handling

JeongYong Park·2023년 7월 20일


JWT 로그인 개발을 수행하면서 토큰 검증을 필터단에서 수행하고 있었습니다. 토큰이 없다면 401 HttpStatus 응답코드를 반환하도록 설정했습니다. 그런데 POSTMAN으로 요청을 날려보니 500 응답코드를 반환하고 있었습니다.

왜 그런지 이유를 생각했더니 필터는 Spring Context 밖에서 동작하고 예외가 발생해도 프로젝트에서 설정해 놓은 @RestControllerAdvice 까지 가지 못하기 때문이라고 생각했습니다.

그래서 Web Context 단에서 발생하는 예외를 처리하는 과정을 기록해두려 합니다.

Filter Exception Handling 적용기

먼저 예외를 처리하는 필터를 생성합니다.

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
    • 기존에 상태코드가 500으로 나갔었는데 이를 수정하기 위해 작성
  • 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 가 웹 서버까지 제어가 가능해 서블릿 필터의 빈을 찾으면 서블릿 필터 체인에 필터를 등록해주게 됩니다.

profile
다음 단계를 고민하려고 노력하는 사람입니다

2개의 댓글

comment-user-thumbnail
2023년 7월 20일

아주 유익한 내용이네요!

1개의 답글