개발 기록
> [Spring Security] - Filter Chain 생성과 호출 본문

환경
java 17
spring version 6.0.11
spring boot version 3.1.3
■
0. Spring Security FIlter 전체 흐름

■
1. Spring Security Filter - DelegationFilterProxy
(1) DelegationgFilterProxy 는 무엇인가?
서블릿 필터를 구현한 Spring Bean에 모든 작업을 위임 하는 대리자
https://www.baeldung.com/spring-delegating-filter-proxy

▶배경:
서블릿 컨테이너는 자체 표준을 사용하여 필터 인스턴스를 등록할 수 있지만, Spring에서 정의한 Bean을 인식하지 못한다.
▶ 원인:
Spring Bean은 스프링 컨테이너에서 생성 및 관리하는 컴포넌트들이고, ServletFilter는 서블릿 컨테이너에서 생성 및 관리하는 필터들이기 때문에 Spring Bean으로 Injection하거나 Spring에서 사용되는 기술을 Servlet에서 사용할 수 없다.
▶ 해결 : DelegatingFilterProxy
서블릿 필터는 DelegatingFilterProxy 를 사용해서 서블릿 필터를 구현한 Spring Bean에 모든 작업을 위임 한다.
(2) DelegationgFilterProxy 와 Spring Security
DelegatingFilterProxy는 "springSecurityFilterChain" 이름의 빈을 ApplicationContext 에서 찾아, 해당 빈에게 요청을 위임하는 역할을 수행한다. 이때 "springSecurityFilterChain"이라는 이름을 가진 빈이 바로 FilterChainProxy 이다.
그 후에 FilterChainProxy Bean에 요청을 전달한다. 이제 FilterChainProxy에서 필터들을 이용하여 보안처리를 진행한 후 최종적으로 SpringMVC의 DeispatcherServlet에 전달하여 요청에 대한 Servlet 처리를 하게 된다.
(3) DelegationgFilterProxy 는 어디서 등록되는가?
- @CoditionalOnBean(name= DEFAULT_FILTER_NAME )
- springSecurityFilterChain 이라는 이름을 가진 Bean 이 존재한다면 DelegatingFilterProxyRegistrationBean을 bean으로 등록하는데 그때 DEFAULT_FILTER_NAME은 springSecurityFilterChain으로 전달 한다.


2. DelegatingFilterProxyRegistrationBean
☞ DelegatingFilterProxyRegistrationBean의 역할은 DelegatingFilterProxy를 등록하는 것이다.
☞ DelegatingFilterProxyRegistrationBean에 getFilter가 호출될 때 DelegatingFilterProxy를 생성하게 된다.


■
2. Spring Security Filter - FilterChainProxy
(1) FilterChainProxy 무엇인가?
▶ SpringSecurityFilterChain의 이름으로 생성되는 필터 빈
▶ SecurityFilterChain을 순회하면서 제공된 URL(RequestMatcher)내용이 일치하는 필터체인을 반환하고 이를 doFilter를 통해 필터링을 진행한다
▶ 각 필터들을 순서대로 호출하며 인증/인가처리 및 각종 요청에 대한 처리를 수행한다


■
3. Spring Security Filter - FilterChainProxy 생성과정과 SecurityFilterChain
(1) Class 다이어그램

(2) FilterChainProxy 생성 과정
- springSecurityFilterChain Bean 등록
- webSecurity.build() 메서드는 상속과 구현을 거쳐서, WebSecurity Class 의 performBuild 메서드를 호출하고, FilterChainProxy의 생성은 performBuild 메서드 내부에서 이뤄진다.

2. WebSecurity
- SecurityFilterChain 을 매개변수로, FilterChainProxy 를 생성한다.
- FilterChainProxy 는 Filter 목록을 가지게 된다.

* FIlterChain
public class FilterChainProxy extends GenericFilterBean {
private List<SecurityFilterChain> filterChains;
public FilterChainProxy(SecurityFilterChain chain) {
this(Arrays.asList(chain));
}
public FilterChainProxy(List<SecurityFilterChain> filterChains) {
this.filterChains = filterChains;
}
(3) SecurityFilterChain 생성 과정
* WebSecurity의 build()가 반환하는 것은 FilterChainProxy이며, HttpSecurity의 build()가 반환하는 것은 SecurityFilterChain 이다.
1. HttpSecurity
- SecurityFilterChain 를 생성하여 WebSecurity에 전달한다.
- HttpSecurity.build()부분을 따라가다보면 다음과같이 DefaultSecurityFilterChain을 반환해준다 즉 Bean으로 등록하는 SecurityFilterChain은 결국 DefaultSecrutiyFilterChain이다.

이렇게 만들어진 SecurityFilterChain은 WebSecurityConfiguration의 setFilterChains()로 주입된다.

2. HttpSecurity 기타 메서드 및 필드
☞ addFilter() :Spring Security에서 제공하는 필터 중 하나의 인스턴스이거나 확장되어야 하는 필터를 추가한다.

☞ filterOrders : 기본적으로 등록된 필터들이 순서대로 저장되어있기 때문에 필터체인의 순서를 가져오거나 확인하는데 사용된다.
private FilterOrderRegistration filterOrders = new FilterOrderRegistration();
@SuppressWarnings("serial")
final class FilterOrderRegistration {
private static final int INITIAL_ORDER = 100;
private static final int ORDER_STEP = 100;
private final Map<String, Integer> filterToOrder = new HashMap<>();
FilterOrderRegistration() {
Step order = new Step(INITIAL_ORDER, ORDER_STEP);
put(DisableEncodeUrlFilter.class, order.next());
put(ForceEagerSessionCreationFilter.class, order.next());
put(ChannelProcessingFilter.class, order.next());
order.next(); // gh-8105
put(WebAsyncManagerIntegrationFilter.class, order.next());
put(SecurityContextHolderFilter.class, order.next());
put(SecurityContextPersistenceFilter.class, order.next());
put(HeaderWriterFilter.class, order.next());
put(CorsFilter.class, order.next());
put(CsrfFilter.class, order.next());
put(LogoutFilter.class, order.next());
this.filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
order.next());
this.filterToOrder.put(
"org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter",
order.next());
put(X509AuthenticationFilter.class, order.next());
put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
this.filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next());
this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",
order.next());
this.filterToOrder.put(
"org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter",
order.next());
put(UsernamePasswordAuthenticationFilter.class, order.next());
order.next(); // gh-8105
put(DefaultLoginPageGeneratingFilter.class, order.next());
put(DefaultLogoutPageGeneratingFilter.class, order.next());
put(ConcurrentSessionFilter.class, order.next());
put(DigestAuthenticationFilter.class, order.next());
this.filterToOrder.put(
"org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter",
order.next());
put(BasicAuthenticationFilter.class, order.next());
put(RequestCacheAwareFilter.class, order.next());
put(SecurityContextHolderAwareRequestFilter.class, order.next());
put(JaasApiIntegrationFilter.class, order.next());
put(RememberMeAuthenticationFilter.class, order.next());
put(AnonymousAuthenticationFilter.class, order.next());
this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
order.next());
put(SessionManagementFilter.class, order.next());
put(ExceptionTranslationFilter.class, order.next());
put(FilterSecurityInterceptor.class, order.next());
put(AuthorizationFilter.class, order.next());
put(SwitchUserFilter.class, order.next());
}
}
■
3. 기타 관련 Class
(1) HttpSecurityBuilder
SecurityBuilder는 아래와 같이 SecurityConfigurer를 포함하고 있으며,
인증 및 인가 초기화 작업은 SecurityBuilder 내부에서 SecurityConfigurer를 통해 진행된다.
public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
extends SecurityBuilder<DefaultSecurityFilterChain> {
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(Class<C> clazz);
.
.
.
H authenticationProvider(AuthenticationProvider authenticationProvider);
H userDetailsService(UserDetailsService userDetailsService) throws Exception;
H addFilterAfter(Filter filter, Class<? extends Filter> afterFilter);
H addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter);
/**
* <ul>
* <li>{@link ForceEagerSessionCreationFilter}</li>
* <li>{@link DisableEncodeUrlFilter}</li>
* <li>{@link ChannelProcessingFilter}</li>
* <li>{@link SecurityContextPersistenceFilter}</li>
* <li>{@link LogoutFilter}</li>
* <li>{@link X509AuthenticationFilter}</li>
* <li>{@link AbstractPreAuthenticatedProcessingFilter}</li>
* <li><a href="
* {@docRoot}/org/springframework/security/cas/web/CasAuthenticationFilter.html">CasAuthenticationFilter</a></li>
* <li>{@link UsernamePasswordAuthenticationFilter}</li>
* <li>{@link org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter}</li>
* <li>{@link org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter}</li>
* <li>{@link ConcurrentSessionFilter}</li>
* <li>{@link DigestAuthenticationFilter}</li>
* <li>{@link BearerTokenAuthenticationFilter}</li>
* <li>{@link BasicAuthenticationFilter}</li>
* <li>{@link RequestCacheAwareFilter}</li>
* <li>{@link SecurityContextHolderAwareRequestFilter}</li>
* <li>{@link JaasApiIntegrationFilter}</li>
* <li>{@link RememberMeAuthenticationFilter}</li>
* <li>{@link AnonymousAuthenticationFilter}</li>
* <li>{@link SessionManagementFilter}</li>
* <li>{@link ExceptionTranslationFilter}</li>
* <li>{@link FilterSecurityInterceptor}</li>
* <li>{@link SwitchUserFilter}</li>
* </ul>
*/
H addFilter(Filter filter);
}
(2) HttpSecurityConfiguration

■
4. Security Filter
| SecurityContextPersistenceFilter | SecurityContextRepository에서 SecurityContext를 가져오거나 저장하는 역할 |
| SecurityContextHolderFilter | SecurityContextRepository에서 SecurityContext를 로드하여 세션에 저장 |
| SessionManagementFilter | 다중 세션을 처리하거나 세션ID를 변경하는 등 세션 정책에 따라 세션을 처리하는 필터. ( 사용자와 관련된 모든 세션을 추적 ) |
| LogoutFilter | 설정된 로그아웃 URL로 오는 요청을 감시하며, 해당 유저를 로그아웃 처리 |
| AuthenticationFilter | 리소스에 대한 접근권한을 부여하는 인가필터 AuthorizationManager에게 인가 프로세스 위임 |
| CORSFilter | CORS정책 체크 필터, CorsProcessor를 호출하여 처리 |
| CSRFilter | CSRF공격 대응 필터. |
| ExceptionTranslationFilter | 필터체인 내에서 발생한 예외를 잡아서 처리하는 필터 (401,403) |
| AnonymousAuthenticationFilter | SecurityFilterChain의 마지막 인증필터로, 이 시점까지 인증을 시도하지 않았다면 익명 사용자 인증객체를 만들어 SecurityContext에 담아 SecurityContextHolder에 저장한다. |
| ConcurrentSessionFilter | 동시세션을 처리 필터, sessionRegistry에 같은 세션이 있는지 조회하여 동시세션을 찾고 세션을 갱신하거나 만료된 세션의 사용자를 로그아웃시킨다. |
|
RememberMeAuthenticationFilter
|
세션이 만료되더라도 요청헤더에 포함된 remember-me토큰을 바탕으로 인증을 수행. 세션이 만료되어 SecurityContext 안의 Authentication객체가 null이 되면 필터가 동작 |
|
BasicAuthenticationFilter
|
Basic 인증방식(ex. base64 인코딩)의 인증요청을 처리하는 인증필터 . |
|
UsernamePasswordAuthenticationFilter
|
(아이디와 비밀번호를 사용하는 form 기반 인증) 설정된 로그인 URL로 오는 요청을 감시하며, 유저 인증 처리 (AuthenticationManager를 통한 인증 실행) |
RequestCacheAwareFilter : 로그인 성공 후, 원래 요청 정보를 재구성하기 위해 사용된다.
SecurityContextHolderAwareRequestFilter : 필터 체인상의 다음 필터들에게 부가정보를 제공한다.
FilterSecurityInterceptor : 이 필터는 AccessDecisionManager 로 권한부여 처리를 위임
WebAsyncManagerIntegrationFilter : 비동기 요청에서도 SecurityContext를 사용할 수 있도록 하는 필터
HeaderWriterFilter : 응답에 보안 관련 헤더를 추가하는 필터
AbstractPreAuthenticatedProcessingFilter : pre-authenticated된 인증요청을 처리하는 필터
DigestAuthenticationFilter : Digest 암호화 방식(ex. MD5, SHA)의 인증요청을 처리하는 인증필터
등등
참고
https://ttl-blog.tistory.com/1165#%F0%9F%A7%90%20SecurityBuilder%20%26%20SecurityConfigurer-1
https://spring.io/guides/topicals/spring-security-architecture/
'Spring' 카테고리의 다른 글
| > [Spring] Request Value 조작 - 1. HandlerMethodArgumentResolver 를 직접 구현해보자! (0) | 2023.12.07 |
|---|---|
| > [Spring] Bean 생명 주기에 사용자 정의 작업 연결 - @PostConstruct, @PreDestory (0) | 2023.12.01 |
| > [Spring Batch] Job, Step 속성 (0) | 2022.10.28 |
| > [AOP] 3. 포인트컷(PointCut)의 다양한 표현식 (0) | 2022.10.27 |
| > [Spring Batch] 스프링 배치 - 1. 입문 (개념과 흐름 파악) (0) | 2022.10.25 |