이 섹션은 서블릿 기반 애플리케이션에서 Spring Security의 상위 수준 아키텍처에 대해 설명합니다.
이 상위 수준의 이해를 바탕으로 참조 문서의 Authentication(인증), Authorization(인가), Protection Against Exploits(공격에 대한 보호) 섹션을 구축합니다.
Spring Security의 서블릿 지원은 Servlet Filter를 기반으로 하기에 먼저 Filter의 역할을 살펴보는 것이 도움이 된다. 다음 이미지는 단일 HTTP 요청에 대한 핸들러의 일반적인 계층화를 보여준다

클라이언트가 애플리케이션에 요청을 보내면, 컨테이너는 요청 URL의 경로를 기반으로 HttpServletRequest를 처리하는 Filter 인스턴스와 Servlet을 포함하는 FilterChain을 생성한다.
Spring MVC 애플리케이션에서 Servlet은 DispatcherServlet의 인스턴스 입니다.
중요:
하나의 Servlet은 하나의 HttpServletRequest와 HttpServletResponse를 처리합니다.
그러나Filter는 여러개가 사용될 수 있으며 다음과 같이 사용될 수 있습니다:
filter 인스턴스나 Servlet이 호출되지 않도록 방지합니다. 이 경우 Filter는 일반적으로 HttpServletResponse를 작성합니다.Filter인스턴스와 Servlet에서 사용하는 HttpServletRequest 또는 HttpServletResponse를 수정합니다.Filter의 강력함은 그것에 전달되는 FilterChain에서 나옵니다.public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 애플리케이션의 나머지 부분 이전에 무언가를 수행
chain.doFilter(request, response); // 애플리케이션의 나머지 부분 호출
// 애플리케이션의 나머지 부분 이후에 무언가를 수행
}
Filter는 하위 Filter 인스턴스와 Servlet 에만 영향을 미치므로, 각 Filter가 호출되는 순서는 매우 중요합니다.
Spring은 Servlet 컨테이너의 생명주기와 Spring의 ApplicationContext사이를 연결할 수 있는DelegatingFilterProxy라는 Filter구현체를 제공합니다.
Servlet 컨테이너는 자체 표준을 사용하여 Filter인스턴스를 등록할 수 있지만, Spring에서 정의한 Bean을 인식하지 못합니다.
표준 Servlet 컨테이너 메커니즘을 통해 DelegatingFilterProxy를 등록할 수 있지만, 모든 작업을 Filter를 구현하는 Spring Bean에 위임한다
다음은 DelegatingFilterProxy가 Filter 인스턴스와 FilterCain에 어떻게 들어맞는지 보여주는 그림입니다.

DelegatingFilterProxy는 ApplicationContext에서 Bean Filter()를 찾은 다음 Bean Filter()를 호출합니다.
다음은 DelegatingFilterProxy의 의사코드입니다:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
Filter delegate = getFilterBean(someBeanName); // (1)
delegate.doFilter(request, response); // (2)
(1) Spring Bean으로 등록된 Filter를 지연(lazy) 조회합니다. 예제에서 delegate는 Bean Filter0의 인스턴스 입니다.
(2) Spring Bean에 작업을 위임합니다.
DelegateFilterProxy의 또 다른 이점은 Filter Bean 인스턴스 조회를 지연시킬 수 있다는 점입니다. 이것은 컨테이너가 시작되기 전에 Filter인스턴스를 등록해야 하기 때문에 중요합니다. 그러나 Spring은 일반적으로 ContextLoaderListener를 사용하여 Spring Bean을 로드하는데, 이는 Filter인스턴스가 등록한 후에 수행됩니다.
Spring Security의 Servlet 지원은 FilterChainProxy내에 포함되어 있습니다.
FilterChainProxy는 Spring Security에서 제공하는 특별한 Filter로, SecurityFilterChain을 통해 많은 Filter인스턴스에 위임할 수 있습니다.
FilterChainProxy는 Bean이므로, 일반적으로 DelegatingFilterProxy로 래핑 됩니다.
다음 이미지는 FilterChainProxy의 역할을 보여줍니다.

SecurityFilterChain은 FilterChainProxy에 의해 사용되며, 현재 요청에 대해 어떤 Spring Security Filter인스턴스들이 호출되어야 하는지를 결정합니다.
다음 이미지는 SecurityFilterChain의 역할을 보여줍니다.

SecurityFilterChain의 Security Filter들은 일반적으로 Bean이지만, DelegatingFilterProxy대신 FilterChainProxy에 등록됩니다.
FilterChainProxy는 Servlet컨테이너 또는 DelegatingFilterProxy에 직접 등록하는 것 에 비해 여러가지 이점을 제공합니다.
첫째,Spring Security의 모든 Servlet 지원에 대한 시작점을 제공합니다. 따라서 Spring Security의 Servlet 지원 문제를 해결하려면 FilterChainProxy에 디버그 포인트를 추가하는 것이 좋은 시작점입니다.
둘째, FilterChainProxy는 Spring Security 사용의 핵심이므로, 선택 사항으로 보이지 않는 작업을 수행할 수 있습니다. 예를 들어, 메모리 누수 방지를 위해 SecurityContext를 정리합니다. 또한 특정 유형의 공격으로부터 애플리케이션을 보호하기 위해 Spring Security의 HttpFirewall을 적용합니다.
셋째,SecurityFilterChain이 언제 호출되어야 하는지를 결정하는데 더 많은 유연성을 제공합니다. Servlet 컨테이너에서 Filter인스턴스는 URL만 기반으로 호출됩니다. 그러나 FilterChainProxy는 RequestMacher인터페이스를 사용하여 HttpServletRequest의 모든 것을 기반으로 호출을 결정할 수 있습니다.
다음이미지는 여러 SecurityFilterChain인스턴스를 보여줍니다.

Multiple SecurityFilterChain 그림에서, FilterChainProxy는 어떤 SecurityFilterChain을 사용해야 하는지 결정합니다. 첫 번째로 매칭되는 SecurityFilterChain만 호출됩니다.
/api/message/ URL이 요청되면, 먼저 /api/**패턴의 SecurityFilterChain0만 호출됩니다
/messagesURL이 요청되면, /api/**패턴의 SecurityFilterChain0과 매칭되지 않으므로, FilterChainProxy는 각SecurityFilterChain을 계속 시도합니다. 다른 SecurityFilterChain인스턴스가 매칭되지 않는다고 가정하면, SecurityFilterChain이 호출됩니다.
SecurityFilterChain0에는 3개의 Security Filter인스턴스만 구성되어 있습니다. 그러나 SecurityFilterChainn에는 4개의 Security Filter인스턴스가 구성되어있습니다.
각 SecurityFilterChain은 고유할 수 있으며 독립적으로 구성될 수 있다는 점에 주목하는 것이 중요합니다. 실제로, 애플리케이션이 Spring Security가 특정 요청을 무시하기를 원한다면, SecurityFilterChain에 보안 Filter인스턴스가 0개일 수도 있습니다.
Security Filter들을 SecurityFilterChainAPI를 통해 FilterChainProxy에 삽입됩니다.
이러한 필터들을 여러가지 목적으로 사용될 수 있습니다:
Filter는 인가를 수행하는 Filter보다 먼저 호출되어야 합니다.일반적으로 Spring Security의 Filter들의 순서를 알 필요는 없습니다. 그러나 순서를 알면 유익한 경우가 있습니다. 순서를 알고 싶다면 FilterOrderRegistration코드를 확인할 수 있습니다.
이러한 Security Filter들은 대부분 HttpSecurity인스턴스를 사용하여 선언됩니다.
위 단락을 예시로 들기 위해, 다음 보안 설정을 고려해보겠습니다:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults())
.formLogin(Customizer.withDefaults())
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
);
return http.build();
}
}
위 설정은 다음과 같은 Filter 순서를 생성합니다:
| Filter | Added by |
|---|---|
| CsrfFilter | HttpSecurity#csrf |
| UsernamePasswordAuthenticationFilter | HttpSecurity#formLogin |
| BasicAuthenticationFilter | HttpSecurity#httpBasic |
| AuthorizationFilter | HttpSecurity#authorizeHttpRequests |