[Spring Security] 아키텍처 이해 - 위임필터 및 빈 초기화

식빵·2022년 8월 15일
0
post-thumbnail

이 시리즈에 나오는 모든 내용은 인프런 인터넷 강의 - 스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security - 에서 기반된 것입니다. 그리고 여기서 인용되는 PPT 이미지 또한 모두 해당 강의에서 가져왔음을 알립니다.



스프링 시큐리티가 동작하기 위해서 가장 근본적으로 필요한
DelegatingProxyChain, FilterChainProxy 에 대해서 알아보자.




🥝 DelegatingFilterProxy


Servlet Filter 는 Servlet 의 기술이지, 스프링의 기술이 아니다.
그렇기 때문에 스프링의 핵심 기술인 DI(의존성 주입) 사용에 어려움이 따른다.

이를 위해서 스프링은 DelegatingFilterProxy라는 것을 만들었다.
이 클래스의 특징은 다음과 같다.

  • class GenericFilterBean implements Filter를 Extend 한 클래스로 일반적인 필터다.
  • 초기화 시, 미리 지정한 targetBeanName을 기준으로 빈 객체를 찾아서 내부에 저장한다.
    • 이때 가져온 빈 객체 또한 Filter 를 구현한 객체이다.
  • 이후 필터에서 요청이 들어오면 앞서 미리 가져온 targetBean 에게 요청을 위임한다.

참고: GenericFilterBean Java Document

Simple base implementation of Filter which treats its config parameters
(init-param entries within the filter tag in web.xml) as bean properties.

A handy superclass for any type of filter. Type conversion of config parameters is automatic, with the corresponding setter method getting invoked with the converted value. It is also possible for subclasses to specify required properties. Parameters without matching bean property setter will simply be ignored.

This filter leaves actual filtering to subclasses, which have to implement the Filter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) method.

This generic filter base class has no dependency on the Spring ApplicationContext concept. Filters usually don't load their own context but rather access service beans from the Spring root application context, accessible via the filter's ServletContext (see WebApplicationContextUtils).


참고2: DelegatingFilterProxy는 스프링 시큐리티에서만 쓰는 게 아니다!




🥝 FilterChainProxy


DelegatingFilterProxy 는 맨 처음 요청을 받을 때, 자신이 저장하고 있던
targetBeanName 문자열 필드값과 매칭되는 빈 객체를 Application Context 찾는다.

스프링 시큐리티에서 DelegatingFilterProxy를 사용할 때는 기본적으로targetBeanName="springSecurityFilterChain" 으로 세팅되어 있다.

이때 해당 빈 이름으로 ApplicationContext 에서 찾아낸 빈의 클래스가 FilterChainProxy이다.

DelegatingFilterProxy단순히 요청을 위임하는 것이였다면,
FilterChainProxy실제 보안처리가 시작되는 지점이다.

실제 보안 처리를 위해서 FilterChainProxy 내부에 여러 보안 필터를 List 로 갖는다.
이 필터들은 스프링 시큐리티 초기화시 생기는 필터개발자가 설정한 필터들이다.
우리가 여태 봐었던 스프링 시큐리티 제공 필터들이 다 이 안에 있다.

이후에 사용자 요청이 오면 이 Filter List 에 있는 필터를 차례대로 호출한다.
참고로 Filter List 사이에 직접 필터를 넣을 수도 있다.
이때 순서를 잘 생각해서 넣어야 한다.


참고 사진) FilterChainProxy의 내부 필터 목록




🥝 사용자 요청의 흐름


  1. Client 요청 발생
  2. DelegatingFilterProxy 필터가 요청을 가로챔
  3. DelegatingFilterProxy 에 내장된 FilterChainProxy 타입의 빈 객체에게 요청 위임
  4. FilterChainProxy 에 내장된 Spring Security 필터 list 를 순회하면서 필터 실행




🥝 초기화 코드 추적 (중요X, 헷갈림 주의)


1. 스프링 부트 자동설정

스프링 시큐리티의 자동 초기화 클래스 내부의 초기화 메소드들이 실행된다.
여기서 집중할 것은 @ConditionalOnBean(name = DEFAULT_FILTER_NAME)에서
DEFAULT_FILTER_NAME="springSecurityFilterChain" 이라는 점이다.

@ConditionalOnBean 는 BeanFactory 에서 지정한 name으로 된 빈 객체를 찾아내면
@Bean 메소드를 수행하여 Bean 객체를 생성하는 애노테이션이다.

만약 name 에 해당하는 빈 객체를 찾으면 DelegatingFilterProxy (=Filter) 를
생성 및 내장 WAS 에 등록해주는 빈 객체가 생성된다.


2. DelegatingFilterProxy 생성

실제로 DelegatingFilterProxy 가 생성되는 시점 Application Context 에서
찾을 targetBeanName 이 지정되는 것을 확인할 수 있다.



3. springSecurityFilterChain 빈 객체 생성

WebSecurityConfiguration 라는 설정 클래스의 springSecurityFilterChain 빈 생성 메소드가 실행된다. 이 메소드의 맨 마지막 this.webSecurity.build() 메소드가 실제 빈 객체 생성하고, 그 객체의 타입은 FilterChainProxy 이다. 해당 메소드를 계속 추적하면 아래와 같은 코드가 나온다.


실제 FilterChainProxy 인스턴스를 생성해주는 걸 확인할 수 있다.

여기까지 오면 일단 DelegatingFilterProxy, springSecurityFilterChain 빈 객체
모두 생성이 된 것이다. 하지만 아직 연결은 안됐다.
연결은 맨 처음 DelegatingFilterProxy 에 요청이 왔을 때이다.





🥝 요청 수행 코드 추적


1. DelegatingFilterChain.doFilter

맨 처음 요청이 오면 if(delegateToUse == null) 분기문 내부로 들어간다.
그리고 이 과정에서 initDelegate 메소드가 수행된다.


이 메소드 내용을 보면 알겠지만 springSecurityFilterChain 이라는 이름의 빈 객체를 찾아서
위임 빈 객체로 지정한다.

이렇게 모든 초기화가 끝나면 그제서야 invokeDelegate 메소드를 실행하여
실제 springSecurityFilterChain 빈 객체에 요청을 위임하게 된다.
다시 말하지만 springSecurityFilterChainFilterChainProxy 클래스 인스턴스다.



2. FilterChainProxy.doFilter

VirtualFilterChain 를 생성해서 해당 객체에 모든 요청 처리를 위임한다.
new VirtualFilterChain(filters) 의 인자값 filters는 스프링 시큐리티의 보안 필터 목록들이다.



3. FilterChainProxy$VirtualFilterChain.doFilter

여기서부터 실제 보안 필터들이 수행된다. 끝!

profile
백엔드를 계속 배우고 있는 개발자입니다 😊

0개의 댓글