
스프링 시큐리티 필터체인에 대해 공부하다가 궁금한 점이 문득 생겼다.
커스텀 필터를 시큐리티 필터 체인에 적용하기 위해서, Filter 를 구현할 필터 구현체를 정의했다.
일반적으로 Filter 인터페이스를 구현한 필터 객체를 하나 정의하여 “빈으로 등록하면” 애플리케이션이 실행될 때 자동으로 등록해준다.
하지만 Filter 를 구현할 때 주의해야 하는 점이 있다.
시큐리티 필터 체인에만 등록할 것인지, 애플리케이션 필터 체인에만 등록할 것인지를 잘 정해야 한다.
애플리케이션 필터 체인이란, 서블릿 컨테이너가 (톰캣, Jetty 등) 관리하는 필터 체인을 말한다.
일반적으로, 애플리케이션 필터 체인은 스프링이 제공하는 기술들을 (빈, 의존성 주입 등) 사용할 수 없다.
(하지만 DelegatingFilterProxy 처럼 스프링에서 서블릿 컨테이너와 스프링을 연결시켜주는 다리를 제공한다면 서블릿 영역에서도 스프링 빈을 사용할 수 있게 된다.)
스프링에서는 @WebFilter, OncePerRequestFilter, FilterRegistrationBean 인터페이스 등과 같이 애플리케이션 필터 체인에 필터를 등록할 수 있는 수단을 제공한다.
그렇다면 시큐리티 필터 체인은 뭘까?
스프링 시큐리티가 관리하는 필터체인으로, DelegatingFilterProxy 라는 스프링 부트가 제공하는 기술을 이용하여 서블릿 컨테이너 영역에서도 스프링 컨테이너에 등록된 빈들을 사용할 수 있도록 해준다.

출처 : https://gngsn.tistory.com/160
위 사진을 보면, 서블릿 필터 사이에 DelegatingFilterProxy가 존재하는 것을 볼 수 있다.
DelegatingFilterProxy는 FilterChainProxy 를 참조하고 있어, 스프링 컨테이너에서 개발자가 정의한 필터 Bean 들을 차례로 실행할 수 있도록 한다.
스프링 시큐리티에서 커스텀 필터를 사용하여, 시큐리티 필터체인에 등록하면 바로 저 FilterChainProxy가 가지고 있는 필터들에 등록이 되는 것이다.
시큐리티 필터 체인에 커스텀 필터를 등록할 때 주의해야 하는 사항이 있다.
바로 Filter를 애플리케이션 필터 체인에 등록시키지 않는 것이다.
애플리케이션 필터 체인에 등록하게 된다면, 시큐리티 필터 체인 밖에도 등록되고 시큐리티 필터 체인 안에도 등록이 되므로
필터가 중복해서 동작하거나, 모든 URL 마다 필터가 동작하여 개발자가 의도하지 않은 현상이 발생할 수 있다.
애플리케이션 필터 체인에 등록하지 않는 방법은 스프링에서 제공하는 FilterRegistrationBean 을 사용하는 것이다.
Filter 는 일반적으로 서블릿 컨테이너 영역에 속하며, 스프링이 제공하는 기술을 사용할 수 없다.
(빈 주입 등)
하지만 Filter도 스프링 빈으로 등록하고, 스프링 컨테이너가 제공하는 빈의 라이프 사이클을 제공받을 수 있게 하기 위해서는 FilterRegistrationBean 을 사용하면 된다.
FilterRegistrationBean 을 사용하여 new FilterRegistrationBean<>(myFilter.class) 으로 필터를 등록하는 빈을 생성한다면,ServletContext.addFilter(…) 를 호출하여 실제 서블릿 필터로 등록한다.개발자가 커스텀 필터를 설정하는 FilterReigstrationBean 을 직접 정의하지 않았을 경우에는, 스프링 부트가 자동으로 기본 설정된 FilterRegistrationBean을 사용하여 필터를 등록한다.
개발자가 커스텀 필터에 관한 설정을 하지 않았을 경우에는 FilterRegistrationBean 의 기본 설정을 사용하므로 애플리케이션 필터 체인에 등록된다.
만약 개발자가 정의한 필터가 시큐리티 필터 체인에서만 사용해야 하는 필터일 경우에는 커스텀 필터를 빈으로 등록하는 FilterRegistrationBean을 개발자가 직접 정의하여
애플리케이션 필터 체인으로 등록되지 않도록 해야 한다.
(대신 시큐리티 필터 체인에는 포함되도록 시큐리티 영역에서 설정을 해주어야 한다.)
@Bean
public FilterRegistrationBean<UnknownEndpointFilter> unknownEndpointFilterRegistrationBean(
UnknownEndpointFilter unknownEndpointFilter) {
FilterRegistrationBean<UnknownEndpointFilter> registrationBean =
new FilterRegistrationBean<>(unknownEndpointFilter);
registrationBean.setEnabled(false);
return registrationBean;
}
위 코드를 보면, registrationBean.setEnabled(false); 코드가 존재하는데, 서블릿 컨테이너에 등록할지 말지를 제어하는 플래그이다.
enabled=true 일 경우에 servletContext.addFilter(…) 가 호출되어 필터가 체인에 등록된다.
하지만 enabled=false 일 경우에는 onStartup 호출 시 아무 동작도 하지 않으므로, 해당 필터를 서블릿 필터 체인에 포함되지 않는다.