이번 포스팅에서는 스프링 시큐리티의 주요 아키텍쳐 중 DelegatingFilterProxy와 FilterChainProxy를 정리하겠습니다.
DelegatingFilterProxy
Servlet Filter는 Servlet 스펙이기 때문에 스프링에서 정의된 빈을 주입받아 사용할 수 없습니다. 하지만 보안 정책을 정의하고 사용자 저장소, 기타 등등 여러 위치에서 빈 주입이 반드시 필요합니다.
때문에 스프링 시큐리티는 DelegatingFilterProxy를 통해 서블릿 컨테이너에서 필터로서 요청을 취득하고, 스프링 컨테이너에 존재하는 특정 빈(name = springSecurityFilterChain)을 찾아 요청을 위임합니다.
아래는 DelegatingFilterProxy의 소스코드입니다. (Spring Security 5.6.0 기준)
public class DelegatingFilterProxy extends GenericFilterBean {
...
/**
* 필터가 수행되면 요청을 위임할 Bean을 찾아 요청을 위임합니다.
* 요청을 위임하는 것 외에는 아무런 보안 처리를 하지 않습니다.
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized(this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = this.findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
}
delegateToUse = this.initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
this.invokeDelegate(delegateToUse, request, response, filterChain);
}
...
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
String targetBeanName = this.getTargetBeanName();
Assert.state(targetBeanName != null, "No target bean name set");
Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class);
if (this.isTargetFilterLifecycle()) {
delegate.init(this.getFilterConfig());
}
return delegate;
}
protected void invokeDelegate(Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
}
이 필터가 수행되면 WebApplicationContext에서 미리 정의된 빈 네임(springSecurityFilterChain)을 통해 스프링 시큐리티의 FilterChainProxy 빈을 취득하고, 요청을 위임하게됩니다.
FilterChainProxy
FilterChainProxy는 springSecurityFilterChain 이라는 이름으로 생성되는 필터 Bean 입니다.
앞서 살펴본 DelegatingFilterProxy로부터 요청을 위임받고, 스프링 시큐리티 초기화 시 생성되는 필터들을 관리하고 제어합니다.
이 필터 체인에 등록되는 필터들은 스프링 시큐리티 설정에 따라 다르며, 등록된 모든 필터를 순차적으로 진행했을 때 인증 및 인가 예외가 발생하지 않으면 보안 통과한 것으로 간주합니다.
아래 필터들은 스프링 시큐리티 기본 설정시 자동으로 FilterChainProxy에 추가되는 필터들입니다.
필터명 | 설명 |
WebAsyncManagerIntegrationFilter | Spring Web의 Async Request와 SpringContext를 연결해주는 역할 |
SecurityContextPersistenceFilter | SecurityContextRepository에서 SecurityContext를 로드하고 저장하는 일을 담당 |
HeaderWriterFilter | 현재 응답에 헤더를 추가하기 위해 구현된 필터 X-Frame-Options, X-XSS-Protection, X-Content-Type-Options 등... |
CsrfFilter | CSRF 취약점 방지를 위한 필터 |
LogoutFilter | 로그아웃 URL로 지정된 가상URL에 대한 요청을 감시하고 매칭되는 요청이 있으면 사용자를 로그아웃시킴 |
UsernamePasswordAuthenticationFilter | 사용자명과 비밀번호로 이뤄진 폼기반 인증에 사용하는 가상 URL요청을 감시하고 요청이 있으면 사용자의 인증을 진행함 |
DefaultLoginPageGeneratingFilter | 폼기반 또는 OpenID 기반 인증에 사용하는 가상URL에 대한 요청을 감시하고 로그인 폼 기능을 수행하는데 필요한 HTML을 생성함 |
DefaultLogoutPageGeneratingFilter | 로그아웃 페이지의 HTML을 생성함 |
ConcurrentSessionFilter | 동시 세션 제어 |
RequestCacheAwareFilter | 로그인 성공 이후 인증 요청에 의해 가로채어진 사용자의 원래 요청을 재구성하는데 사용 |
SecurityContextHolderAwareRequestFilter | HttpServletRequest를 HttpServletRequestWrapper를 상속하는 하위 클래스(SecurityContextHolderAwareRequestWrapper)로 감싸서 필터 체인상 하단에 위치한 요청 프로세서에 추가 컨텍스트를 제공함 |
AnonymousAuthenticationFilter | 이 필터가 호출되는 시점까지 사용자가 아직 인증을 받지 못했다면 요청 관련 인증 토큰에서 사용자가 익명 사용자로 나타나게 됨 |
SessionManagementFilter | 인증된 주체를 바탕으로 세션 트래킹을 처리해 단일 주체와 관련한 모든 세션들이 트래킹되도록 도움 |
ExceptionTranslationFilter | 보호된 요청을 처리하는 동안 발생할 수 있는 기대한 예외의 기본 라우팅과 위임을 처리 |
FilterSecurityInterceptor | 권한부여와 관련한 결정을 AccessDecisionManager에게 위임해 권한부여 결정 및 접근 제어 결정을 쉽게 만들어 줌 |
Reference
스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security - 인프런 | 강의
초급에서 중.고급에 이르기까지 스프링 시큐리티의 기본 개념부터 API 사용법과 내부 아키텍처를 학습하게 되고 이를 바탕으로 실전 프로젝트를 완성해 나감으로써 스프링 시큐리티의 인증과
www.inflearn.com
Spring Security Default Filter
Spring Security의 구조를 살펴보면, 사용자의 요청이 들어온 후 가장 먼저 처리되는 곳이 바로 Filter(FilterChain)이다.
smjeon.dev
'🌱 SPRING' 카테고리의 다른 글
[Spring Security] 스프링 시큐리티 주요 아키텍쳐 3 - 인가(Authorization) (0) | 2021.12.16 |
---|---|
[Spring Security] 스프링 시큐리티 주요 아키텍쳐 2 - 인증(Authentication) (0) | 2021.12.10 |
[Spring Cloud Config] 설정값을 외부에서 관리하자! - 실습 (4) | 2021.10.10 |
[Spring Cloud Config] 설정값을 외부에서 관리하자! - 기본 (0) | 2021.10.10 |
[SpringCloud] Resilience4j와 Spring Cloud Circuit Breaker (0) | 2021.05.16 |