[Spring] Filter Exception Handling

JeongYong Park·2023년 7월 20일
1


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개의 답글