[Spring Security] DelegatingFilterProxy 와 FilterChainProxy

2024. 11. 20. 16:32Framework & Library/Spring Security

 지금까지의 Spring Security 초기화 과정을 학습하면서 인증/인가에 사용되는 Filter 들이 어떻게 만들어지는지 알 수가 있었다. 그런데 이렇게 만들어진 Filter 들은 어떻게 사용되는 걸까?

0. Filter (= ServletFilter)

 Spring Security 공식 레퍼런스의 내용과 강의 내용을 비교해보며 정리한 내용을 다음과 같다.

Spring Security’s Servlet support is based on Servlet Filters, so it is helpful to look at the role of Filters generally first. The following image shows the typical layering of the handlers for a single HTTP request.

Spring Security 의 서블릿 지원은 서블릿 필터를 기반으로 한다. 먼저 필터의 역할을 전반적으로 살펴볼 필요가 있다. 아래의 이미지는 단일 HTTP 요청에 대한 핸들러의 일반적인 계층화를 보여준다.

출처 : Spring Security - reference doc

The client sends a request to the application, and the container creates a FilterChain, which contains the Filter instances and Servlet that should process the HttpServletRequest, based on the path of the request URI.

클라이언트가 애플리케이션에 요청을 보내면 컨테이너는 요청 URI 경로에 따라 HttpServletRequest 를 처리해야 하는 필터 인스턴스와 서블릿을 포함하는 FilterChain 을 생성한다.
In a Spring MVC application, the Servlet is an instance of DispatcherServlet. At most, one Servlet can handle a single HttpServletRequest and HttpServletResponse.However, more than one Filter can be used to:
- Prevent downstream Filter instances or the Servlet from being invoked. In this case, the Filter typically writes the HttpServletResponse.
- Modify the HttpServletRequest or HttpServletResponse used by the downstream Filter instances and the Servlet.
The power of the Filter comes from the FilterChain that is passed into it.

Spring MVC 애플리케이션에서 서블릿은 DispatcherServlet 의 인스터스이다. 하나의 서블릿은 최대 하나의 HttpServletRequest 와 HttpServletResponse 를 처리할 수 있다. 하지만 둘 이상의 필터를 사용할 수가 있다:
- 다운스트림 필터 인스턴스 또는 서블릿이 호출되지 않도록 할 경우 필터는 일반적으로 HttpServletRequest 를 작성한다.
- 다운스프림 필터 인스턴스 및 서블릿에서 사용하는 HttpServletRequest 또는 HttpServletResponse 를 수정한다.
필터의 영향력(=힘)은 필터에 전달되는 필터체인에서 나온다.
Since a Filter impacts only downstream Filter instances and the Servlet, the order in which each Filter is invoked is extremely important.

필터는 다운스트림 필터 인스턴스와 서블릿에만 영향을 미치므로 각 필터가 호출되는 순서는 매우 중요하다.

 

여기서 말하는 필터는 '서블릿 필터' 를 말한다. 이 서블릿 필터는 웹 애플리케이션에서 클라이언트의 요청과 서버의 응답을 가공하거나 검사(필터링)하는데 사용되는 구성 요소이며 클라이언트의 요청이 서블릿에 도달하기 전이나 서블릿이 응답을 클라이언트에게 보내기 전에 앞서 말한 작업들을 수행할 수 있다.

 

그리고 알아두어야 할 것은 서블릿 필터는 Spring Container 가 아닌 Servlet Container(= WAS) 에서 생성되고 실행/종료 된다. 즉, WAS(Web Application Server) 에서(의해) 관리된다.

 

그렇다보니 찾아본 정보나 강의에서는 Filter 에 Spring 의 주요 기술들이 적용되지 않는다고 말한다. 앞서 말한 것처럼 Filter 는 Spring Container 가 아닌 Servlet Container 에 의해 관리되기 때문이다. 그렇다면 이전에 Spring Security 초기화 작업을 통해 생성한 필터들 또한 Servlet Container 에 의해 관리되는 것일까?

 

 

1. DelegatingFilterProxy

 위의 질문에 대한 답을 한다면 "아니다" 이다. Spring 은 Servlet Container 생명주기와 Spring 의 ApplicationContext 간에 다리 역할을 가능하게 하는 DelegatingFilterProxy 라는 이름의 필터 구현을 제공한다. 그렇다면 Spring Security 레퍼런스에 작성된 내용을 통해 DelegatingFilterProxy 에 대해서 더 알아보자.

Spring provides a Filter implementation named DelegatingFilterProxy that allows bridging between the Servlet container’s lifecycle and Spring’s ApplicationContext. The Servlet container allows registering Filter instances by using its own standards, but it is not aware of Spring-defined Beans. You can register DelegatingFilterProxy through the standard Servlet container mechanisms but delegate all the work to a Spring Bean that implements Filter.
Here is a picture of how DelegatingFilterProxy fits into the Filter instances and the FilterChain.

Spring 은 Servlet Container 의 생명주기와 Spring 의 ApplicationContext 간에 다리 역할을 가능하게 하는 DelegatingFilterProxy 라는 필터 구현을 제공한다. Servlet Container 는 자체 표준을 사용해 필터 인스턴스를 등록할 수 있지만 Spring 에 정의된 Bean 을 인식하지 못한다. 표준 Servlet Container 매커니즘을 통해 DelegatingFilterProxy 를 등록하지만 Filter 의 모든 작업은 Filter 를 구현한 Spring Bean 에 위임할 수 있다.
다음은 DelegatingFilterProxy 가 필터 인스턴스와 필터 체인에 어떻게 적용되는지 보여주는 그림이다.

출처 : Spring Security - reference doc

 

즉, Spring Security 초기화에서 생성된 SecurityFilterChain 을 ApplicationFilterChain(위 이미지의 FilterChain 으로 Servlet Container 가 관리함) 에서 직접 사용하는 것이 아니라 DelegatingFilterProxy 라는 필터를 통해 사용된다는 것을 알 수 있다.

 

DelegatingFilterProxy looks up Bean Filter0 from the ApplicationContext and then invokes Bean Filter0.
- Lazily get Filter that was registered as a Spring Bean. For the example in DelegatingFilterProxy delegate is an instance of Bean Filter.
- Delegate work to the Spring Bean.

DelegatingFilterProxy 는 ApplicationContext 에서 Bean Filter() 를 조회한 다음 Bean Filter() 를 호출한다.
- Spring Bean 으로 등록된 필터를 느리게 가져온다(지연시켜 가져온다는 의미인 듯). 이 때 DelegatingFilterProxy 의 위임자는 Bean Filter() 의 인스턴스이다.
- Spring Bean 에 작업을 위임한다.

 

FilterChain(=ApplicationFilterChain) 을 통해 DelegatingFilterProxy 에 접근하게 되면 해당 필터가 가지고 있는 Bean Filter 를 조회한 후 호출해 필터 작업을 수행한다. 즉, 필터 작업을 Spring Bean 에 위임하는 것이다.

 

결국 DelegatingFilterProxy 는 스프링에서 사용되는 특별한 서블릿 필터이고, Servlet Container 와 Spring 의 Application Context 간의 연결고리 역할을 하는 필터라는 것을 알 수 있었다. 특별한 이유는 서블릿 필터의 기능을 수행하며 스프링의 의존성 주입 및 빈 관리 기능과 연동되도록 설계된 필터이기 때문이다. 

 

그렇다면 DelegatingFilterProxy 는 실제 보안 처리를 수행하지 않고 위임한다고 하는데 어떤 방식으로 위임하게 될까? DelegatingFilterProxy 는 "springSecurityFilterChain" 이란 이름으로 생성된 Bean 을 ApplicationContext 에서 찾아 실제 보안 처리 작업을 위임한다. 근데 지금까지 위와 같은 이름의 빈을 등록한 적이 없는데 어떻게 알고 Bean 을 찾는 걸까?

 

Spring Security 의 초기화는 결국 FilterChainProxy 를 생성해 Bean 으로 등록하기 위함이라고 강의를 통해서 알게되었다. 그런데 이 FilterChainProxy 를 Bean 으로 등록될 때 "springSecurityFilterChain" 이라는 이름으로 등록하고 있었다.

WebSecurityConfiguration.springSecurityFilterChain()
AbstractSecurityWebApplicationInitializer 의 DEFAULT_FILTER_NAME

 

WebSecurityConfiguration 클래스의 springSecurityFilterChain() 는 FilterChainProxy 를 생성해 Bean 으로 등록하는데 잘 보면 @Bean 의 name 속성을 보면 AbstractSecurityWebApplicationInitializer 클래스의 상수가 지정된 것을 볼 수 있는데 해당 상수가 "springSecurityFilterChain" 을 값으로 가지고 있기에 FilterChainProxy 가 해당 이름으로 Bean 등록이 된다.

 

 

2. FilterChainProxy

 Spring Security 초기화의 목적인 FilterChainProxy 는 어떤 녀석일까?

Spring Security’s Servlet support is contained within FilterChainProxy. FilterChainProxy is a special Filter provided by Spring Security that allows delegating to many Filter instances through SecurityFilterChain. Since FilterChainProxy is a Bean, it is typically wrapped in a DelegatingFilterProxy.
The following image shows the role of FilterChainProxy.

Spring Security 의 서블릿 지원은 FilterChainProxy 에 포함되어 있다. FilterChainProxy 는 Spring Security 에서 제공하는 특수한 필터로 SecurityFilterChain 을 통해 여러 필터 인스터스에 위임할 수 있다. FilterChainProxy 는 Bean 이기에 DelegatingFilterProxy 에 래핑된다.
아래의 이미지는 FilterChainProxy 의 역할을 보여준다.

출처 : Spring Security - reference doc

 

즉, DelegatingFilterProxy 에 대한 설명을 할 때 사용한 이미지의 Bean Filter 가 FilterChainProxy 이다. 해당 Bean 은 "springSecurityFilterChain" 이란 이름을 가지고 있다. 내부적으로는 하나 이상의 SecurityFilterChain 객체(들을)를 가지고 있으며 요청 URL 별 적절한 SecurityFilterChain 을 선택해 필터들을 호출한다.

 

FilterChainProxy 가 SecurityFilterChain 에 속한 필터들에 접근할 수 있는 이유는 FilterChainProxy 생성시 SecurityBuilder 가 가진 SecurityFilterChain 을 파라미터로 전달받기 때문이다.

 

 

3. 정리

 WAS 에서는 Spring Bean 에 대해 알수 없기 때문에 DelegatingFilterProxy 를 통해 Bean 으로 등록된 필터를 조회할 수 있게 해야한다. 그 이유는 HttpSecurity 를 통해 생성한 SecurityFilterChain 이 Spring Container 의 Bean 으로 등록되어 있기 때문이다.

 

그리고 다수의 SecurityFilterChain 을 요청 URL 에 맞게 사용하기 위해 SecurityFilterChain 을 갖는 FilterChainProxy 가 필요하한 것으로 보인다. 해당 내용은 공식문서나 강의에 나온 내용은 아니지만 왜 FilterChainProxy 가 필요한건지 혼자 고민해보고 내린 결론이다.

 

마지막으로 FilterChainProxy 는 Bean 에 등록된 SecurityFilterChain 들을 가지고 있어 요청에 따라 적절한 SecurityFilterChain 에 접근해 필터 인스턴스를 통해 필터 작업을 처리할 것이다.


참고 강의/문서