| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
- N과M
- Simulation
- dfs
- BOJ
- Queue
- 트리
- 그리디
- 백트래킹
- BFS
- 코딩테스트
- 투포인터
- Stack
- docker
- Spring
- DP
- JPA
- 퀵정렬
- 티스토리챌린지
- programmers
- CS
- 취준
- Java
- Security
- priorityqueue
- 오블완
- 도커교과서
- 구현
- 이진탐색
- 이분탐색
- MySQL
- Today
- Total
Untitled1.class
Spring Security Servlet Applications Architecture 본문
Spring Security의 Servlet 지원은 Servlet Filter에 기반하므로, 먼저 Filter의 역할을 전반적으로 살펴보는 것이 좋다.

Client가 Application에 Request를 보내면 Container는 Request URI 경로를 기반으로 HttpServletRequest를 처리해야 하는 Filter instance와 Servlet을 포함하는 Filter chain을 생성한다. Spring MVC Application에서 Servlet은 DispatcherServlet의 instance이다. 하나의 Servlet은 최대 하나의 HttpServletRequest와 HttpServletResponse를 처리할 수 있다.
두 개 이상의 Filter를 사용하여 다음과 같은 작업을 수행할 수 있다:
- Downstream filter instance 또는 servlet이 호출되는 것을 방지한다. 이 경우 filter는 일반적으로 HttpServletResponse를 작성한다.
- Downstream filter instance와 servlet에서 사용하는 HttpServletRequest 또는 HttpServletResponse를 수정한다.
Filter의 기능은 filter에 전달되는 filter chain에서 비롯된다.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// do something before the rest of the application
chain.doFilter(request, response); // invoke the rest of the application
// do something after the rest of the application
}
Filter는 downstream filter instance와 servlet에만 영향을 미치므로, 각 filter가 호출되는 순서는 매우 중요하다.
DelegatingFilterProxy
Spring은 DelegatingFilterProxy라는 filter 구현체를 제공하며, 이를 통해 Servlet Container의 life cycle과 Spring의 ApplicationContext를 연결할 수 있다. Servlet Container는 자체 표준을 사용하여 filter instance를 등록할 수 있지만, Spring에서 정의한 Bean은 인식하지 못한다. 표준 Servlet Container 매커니즘을 통해 DelegatingFilterProxy를 등록할 수 있지만, 모든 작업은 filter를 구현하는 Spring Bean에 위임한다.

DelegatingFilterProxy는 ApplicationContext에서 Bean Filter0을 조회한 후 Bean Filter0을 호출한다. 다음은 DelegatingFilterProxy의 의사 코드:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
Filter delegate = getFilterBean(someBeanName); // 1
delegate.doFilter(request, response); // 2
}
- Spring Bean으로 등록된 Filter를 Lazy하게 가져온다. DelegatingFilterProxy의 예시에서 delegate는 Bean Filter0의 instance이다.
- Spring Bean에 작업을 위임한다.
DelegatingFilterProxy의 또 다른 장점은 Filter Bean instance 조회를 Lazy하게 할 수 있다는 것이다. Container가 시작되기 전에 Filter instance를 등록해야 하기 때문에 이 기능이 중요하다. 하지만 Spring은 일반적으로 ContextLoaderListener를 사용하여 Spring Bean을 load하는데, 이는 Filter instance가 등록되어야 비로소 완료된다.
FilterChainProxy
Spring Security의 Servlet support는 FilterChainProxy에 포함되어 있다. FilterChainProxy는 Spring Security에서 제공하는 특수 Filter로, SecurityFilterChain을 통해 여러 Filter instance에 위임할 수 있도록 한다. FilterChainProxy는 Bean이므로 일반적으로 DelegatingFilterProxy로 래핑된다.

SecurityFilterChain
SecurityFilterChain은 FilterChainProxy가 현재 Request에 대해 어떤 Spring Security Filter instance를 호출해야 하는지 결정하는 데 사용된다.

SecurityFilterChain의 Security Filter는 일반적으로 Bean이지만 DelegatingFilterProxy 대신 FilterChainProxy에 등록된다. FilterChainProxy는 Servlet Container나 DelegatingFilterProxy에 직접 등록하는 것보다 여러 가지 이점을 제공한다.
- Spring Security의 모든 Servlet support에 대한 시작점을 제공한다. 따라서 Spring Security의 Servlet support 문제를 해결하려는 경우 FilterChainProxy에 debug 지점을 추가하는 것이 좋은 시작점이다.
- FilterChainProxy는 Spring Security 사용의 핵심이므로 선택 사항으로 간주되지 않는 작업을 수행할 수 있다. 예를 들어, 메모리 누수를 방지하기 위해 SecurityContext를 지운다. 또한 Spring Security의 HttpFirewall을 적용하여 특정 유형의 공격으로부터 Application을 보호한다.
또한, SecurityFilterChain을 호출해야 하는 시점을 결정하는 데 더 많은 유연성을 제공한다. Servlet Container에서 Filter instance는 URL만을 기반으로 호출된다. 하지만 FilterChainProxy는 RequestMatcher interface를 사용하여 HttpServletRequest의 모든 내용을 기반으로 호출을 결정할 수 있다.

다중 SecurityFilterChain 그림에서 FilterChainProxy는 어떤 SecurityFilterChain을 사용할지 결정한다. 일치하는 첫 번째 SecurityFilterChain만 호출된다. /api/messages/ URL이 요청되면 먼저 /api/**의 SecurityFilterChain 0 패턴과 일치하므로 SecurityFilterChain n에서도 일치하더라도 SecurityFilterChain 0만 호출된다. /messages/ URL이 요청되면 /api/**의 SecurityFilterChain 0 패턴과 일치하지 않으므로 FilterChainProxy는 각 SecurityFilterChain을 계속 시도한다. 다른 SecurityFilterChain instance가 일치하지 않는다고 가정하면 SecurityFilterChain n이 호출된다.
SecurityFilterChain 0에는 Security Filter instance가 3개만 구성되어 있다. 그러나 SecurityFilterChain n에는 Security Filter instance가 4개 구성되어 있다. 각 SecurityFilterChain은 고유하며 개별적으로 구성될 수 있다는 점에 유의해야 한다. 실제로, Application에서 Spring Security가 특정 Request를 무시하기 원하는 경우 SecurityFilterChain에는 Security Filter instance가 하나도 없을 수 있다.
Security Filters
Security Filter는 SecurityFilterChain API를 통해 FilterChainProxy에 삽입된다. 이러한 Filter는 악용 방지, 인증, 권한 부여 등 다양한 용도로 사용할 수 있다. Filter는 적절한 시점에 호출되도록 특정 순서로 실행된다. 예를 들어, 인증을 수행하는 Filter는 권한을 수행하는 Filter보다 먼저 호출되어야 한다. 일반적으로 Spring Security Filter의 순서를 알 필요는 없다. 하지만 순서를 아는 것이 유용한 경우도 있다. 순서를 알고 싶다면 FilterOrderRegistration Code를 확인하자.
이러한 Security Filter는 대부분 HttpSecurity instance를 사용하여 선언된다. 위 내용을 예시로 다음 Security Configuration을 살펴보겠다:
@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 |
- CSRF 공격으로부터 보호하기 위해 CsrfFilter가 호출된다.
- Request를 인증하기 위해 AuthenticationFilter가 호출된다.
- Request를 승인하기 위해 AuthorizationFilter가 호출된다.
Handling Security Exceptions
ExceptionTranslationFilter는 AccessDeniedException 및 AuthenticationException을 HTTP Response로 변환할 수 있도록 합니다.
ExceptionTranslationFilter는 Security Filter 중 하나로 FilterChainProxy에 삽입된다.

- 먼저 ExceptionTranslationFilter가 FilterChain.doFilter(request, response)를 호출하여 Application의 나머지 부분을 호출한다.
- 사용자가 인증되지 않았거나 AuthenticationException인 경우, Start Authentication을 실행한다.
- SecurityContextHolder가 삭제된다.
- HttpServletRequest는 인증 성공 후 원래 Request를 다시 실행하는 데 사용할 수 있도록 저장된다.
- AuthenticationEntryPoint는 Client에게 자격 증명을 요청하는 데 사용된다. 예를 들어, login page로 redirection하거나 WWW-Authentication Header를 전송할 수 있다.
- 그렇지 않고 AccessDeniedException인 경우, Access Denied를 실행한다. AccessDeniedHandler가 호출되어 액세스 거부를 처리한다.
ExceptionTranslationFilter 의사 코드:
try {
filterChain.doFilter(request, response); // 1
} catch (AccessDeniedException | AuthenticationException ex) {
if (!authenticated || ex instanceof AuthenticationException) {
startAuthentication(); // 2
} else {
accessDenied(); // 3
}
}
- FilterChain.doFilter(request, response)를 호출하는 것은 Application의 나머지 부분을 호출하는 것과 같다. 즉, Application의 다른 부분(FilterSecurityInterceptor 또는 security method)에서 AuthenticationException 또는 AccessDeniedException이 발생하면 여기에서 포착하여 처리한다.
- 사용자가 인증되지 않았거나 AuthenticationException이 발생하면 인증을 시작한다.
- 그렇지 않으면 액세스가 거부된다.
Saving Requests Between Authentication
보안 예외 처리에서 설명한 것처럼, 인증이 없는 Request가 인증이 필요한 resource에 대한 Request인 경우, 인증 성공 후 다시 Request하기 위해 인증된 resource에 대한 Request를 저장해야 한다. Spring Security에서는 RequestCache 구현체를 사용하여 HttpServletRequest를 저장함으로써 이를 구현한다.
Request Cache
HttpServletRequest는 RequestCache에 저장된다. 사용자가 성공적으로 인증되면 RequestCache를 사용하여 원래 Request를 재생한다. RequestCacheAwareFilter는 사용자 인증 후 RequestCache를 사용하여 저장된 HttpServletRequest를 가져오고, ExceptionTranslationFilter는 AuthenticationException을 감지한 후 사용자를 login endpoint로 redirection하기 전에 RequestCache를 사용하여 HttpServletRequest를 저장한다.
기본적으로 HttpSessionRequestCache가 사용된다. 아래 code는 continue라는 매개변수가 있는 경우 저장된 요청의 HttpSession을 확인하는 데 사용되는 RequestCache 구현을 사용자 지정하는 방법을 보여준다.
@Bean
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
requestCache.setMatchingRequestParameterName("continue");
http
// ...
.requestCache((cache) -> cache
.requestCache(requestCache)
);
return http.build();
}
요청 저장 방지
사용자의 인증되지 않은 Request를 Session에 저장하지 않는 데에는 여러 가지 이유가 있다. 해당 저장 공간을 사용자 browser로 이전하거나 DB에 저장할 수 있다. 또는 login 전에 방문하려고 했던 page 대신 항상 home page로 redirection되도록 이 기능을 비활성화할 수도 있다.
이를 위해 NullRequestCache 구현을 사용할 수 있다.
@Bean
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
RequestCache nullRequestCache = new NullRequestCache();
http
// ...
.requestCache((cache) -> cache
.requestCache(nullRequestCache)
);
return http.build();
}
참조
https://docs.spring.io/spring-security/reference/servlet/architecture.html
'공부 > Spring Security' 카테고리의 다른 글
| Method Security (0) | 2025.05.13 |
|---|---|
| Authorize HttpServletRequests (0) | 2025.05.13 |
| Username/Password Authentication (0) | 2025.05.13 |
| Spring Security Authorization Architecture (0) | 2025.05.13 |
| Spring Security Authentication Architecture (0) | 2025.05.13 |
