JWT Refresh Token
에 대해 공부를 하던 도중, 어느 부분에서 doFilter
를 호출하는 것이 아닌, return
을 호출하게 됐다.
필터는 무조건 FilterChain#doFilter()
를 호출하라고 배웠던 것 같은데, return
을 호출하면 어떻게 되는지 감이 잡히질 않았다.
Filter
에 대해서 잠깐 알아보고, return
과 doFilter()
의 차이점에 대해 알아보겠다.
DispatcherServlet
이라고 볼 수 있다.public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {
}
}
GenericFilterBean
은 스프링의 설정 정보를 포함하고 있는 필터이다.
doFilter
메소드를 오버라이드해야된다.
public class TestFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {}
}
OncePerRequestFilter
는 GenericFilterBean
를 상속받은 클래스이며, 요청당 한 번만 실행되는 특성을 가지고 있다.한 번의 요청에 의도치않게 필터가 여러 번 실행되는 것을 방지해준다.
doFilterInternal
메소드를 오버라이드해야된다.
public class TestFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {}
}
/*
이다.@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<Filter> logFilter() {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
bean.setFilter(new LogFilter());
bean.setOrder(1);
bean.addUrlPatterns("/*");
return bean;
}
@Bean
public FilterRegistrationBean<Filter> loginCheckFilter() {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
bean.setFilter(new LoginCheckFilter());
bean.setOrder(2);
bean.setUrlPatterns(List.of("/1", "/2"));
return bean;
}
}
@Order
로 순서를 지정해줄 수 있다.@Slf4j
@Component
@Order(1)
public class LogFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LogFilter init()");
}
@Override
public void destroy() {
log.info("LogFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LogFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LogFilter doFilter End", requestURI);
}
}
}
@WebFilter
를 통해 적용될 URL을 설정해줄 수 있다.@WebFilter + @ServletComponentScan
방식으로도 사용할 수 있지만, 이는 필터간에 순서를 보장하지 않는다.@Slf4j
@WebFilter({"/login", "/items"})
@Component
@Order(1)
public class LogFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LogFilter init()");
}
@Override
public void destroy() {
log.info("LogFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LogFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LogFilter doFilter End", requestURI);
}
}
}
doFilter()
의 호출 횟수가 FilterChain
에 등록된 Filter
의 개수와 같아지는 시점에 서블릿을 호출하게 된다.
doFilter()
의 종료 시점에 서블릿을 호출하는 것이 아니다.doFilter
를 호출한 뒤, return
을 호출하면 서블릿을 호출하지 못하게 되며 그 상태로 필터들이 하나씩 return
되기 시작하면서 default status code
인 200
과 텅 빈 body
가 response
로 client
에게 내려간다.스프링과 스프링 시큐리티에서의 FilterChain
구현체인 VirtualFilterChain
을 보면, 다음과 같이 currentPosition
과 size
(등록된 필터의 개수)가 같을 때 originalChain
의 doFilter()
를 호출한다.
originalChain
은 개발자가 등록한 필터가 들어있지 않은, 기본적으로 등록된 필터 체인을 의미하는 것 같다.다음 필터가 존재하지 않을 시, PassThroughFilterChain
에서 서블릿을 호출하는 모습을 볼 수 있다.
@Component // default order = 2147483647 // 가장 뒤에 위치한 필터
@Slf4j
public class TestFilter1 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
log.info("TestFilter1 호출됨");
//로직이 끝났으니 return 되어 TestFilter1을 호출한 TestFilter2의 chain.doFilter 가 종료된다.
}
}
@Component
@Slf4j
@Order(value = 2147483646)
public class TestFilter2 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
log.info("TestFilter2 호출됨");
chain.doFilter(request, response); // 실행순서 2번
log.info("TestFilter2 호출됨(after DoFilter[1])"); // API 호출 전 실행
chain.doFilter(request, response); // 실행순서 3번 -> API 호출
// 등록한 필터가 3개이므로, 3번의 doFilter가 호출되면 서블릿을 호출하게 된다.
// 만약, doFilter를 여기서 한 번 더 호출하게 된다면, 서블릿을 다시 재호출하게 된다.
log.info("TestFilter2 호출됨(after DoFilter[2])"); // API 호출 후 실행
//모든 로직 실행 후 return -> TestFilter3의 chain.doFilter()로 진행
}
}
@Component
@Slf4j
@Order(value = 2147483645)
public class TestFilter3 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
log.info("TestFilter3 호출됨");
chain.doFilter(request, response); // 실행순서 1번
log.info("TestFilter3 호출됨(after DoFilter)"); // API 호출 후 실행
}
}
https://www.inflearn.com/chats/786858/filter%EB%A5%BC-%EB%93%B1%EB%A1%9D%ED%95%98%EB%8A%94-4%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95
https://developer-ping9.tistory.com/237
https://velog.io/@jmjmjmz732002/Springboot-OncePerRequestFilter-vs-GenericFilterBean