이번 포스팅에서는 스프링 시큐리티의 주요 아키텍쳐 중 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' 카테고리의 다른 글
[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 |