이 시리즈에 나오는 모든 내용은 인프런 인터넷 강의 - 스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security - 에서 기반된 것입니다. 그리고 여기서 인용되는 PPT 이미지 또한 모두 해당 강의에서 가져왔음을 알립니다.
스프링 시큐리티가 동작하기 위해서 가장 근본적으로 필요한
DelegatingProxyChain
, FilterChainProxy
에 대해서 알아보자.
Servlet Filter 는 Servlet 의 기술이지, 스프링의 기술이 아니다.
그렇기 때문에 스프링의 핵심 기술인 DI(의존성 주입) 사용에 어려움이 따른다.
이를 위해서 스프링은 DelegatingFilterProxy
라는 것을 만들었다.
이 클래스의 특징은 다음과 같다.
class GenericFilterBean implements Filter
를 Extend 한 클래스로 일반적인 필터다.targetBeanName
을 기준으로 빈 객체를 찾아서 내부에 저장한다.Filter
를 구현한 객체이다.참고: 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
는 스프링 시큐리티에서만 쓰는 게 아니다!
DelegatingFilterProxy
는 맨 처음 요청을 받을 때, 자신이 저장하고 있던
targetBeanName
문자열 필드값과 매칭되는 빈 객체를 Application Context
찾는다.
스프링 시큐리티에서 DelegatingFilterProxy
를 사용할 때는 기본적으로targetBeanName="springSecurityFilterChain"
으로 세팅되어 있다.
이때 해당 빈 이름으로 ApplicationContext
에서 찾아낸 빈의 클래스가 FilterChainProxy
이다.
DelegatingFilterProxy
는 단순히 요청을 위임하는 것이였다면,
FilterChainProxy
는 실제 보안처리가 시작되는 지점이다.
실제 보안 처리를 위해서 FilterChainProxy
내부에 여러 보안 필터를 List 로 갖는다.
이 필터들은 스프링 시큐리티 초기화시 생기는 필터와 개발자가 설정한 필터들이다.
우리가 여태 봐었던 스프링 시큐리티 제공 필터들이 다 이 안에 있다.
이후에 사용자 요청이 오면 이 Filter List
에 있는 필터를 차례대로 호출한다.
참고로 Filter List
사이에 직접 필터를 넣을 수도 있다.
이때 순서를 잘 생각해서 넣어야 한다.
참고 사진) FilterChainProxy
의 내부 필터 목록
Client
요청 발생DelegatingFilterProxy
필터가 요청을 가로챔 DelegatingFilterProxy
에 내장된 FilterChainProxy
타입의 빈 객체에게 요청 위임FilterChainProxy
에 내장된 Spring Security
필터 list 를 순회하면서 필터 실행스프링 시큐리티의 자동 초기화 클래스 내부의 초기화 메소드들이 실행된다.
여기서 집중할 것은 @ConditionalOnBean(name = DEFAULT_FILTER_NAME)
에서
DEFAULT_FILTER_NAME="springSecurityFilterChain"
이라는 점이다.
@ConditionalOnBean
는 BeanFactory 에서 지정한 name
으로 된 빈 객체를 찾아내면
@Bean
메소드를 수행하여 Bean 객체를 생성하는 애노테이션이다.
만약 name 에 해당하는 빈 객체를 찾으면 DelegatingFilterProxy (=Filter) 를
생성 및 내장 WAS 에 등록해주는 빈 객체가 생성된다.
실제로 DelegatingFilterProxy 가 생성되는 시점 Application Context 에서
찾을 targetBeanName 이 지정되는 것을 확인할 수 있다.
WebSecurityConfiguration
라는 설정 클래스의 springSecurityFilterChain
빈 생성 메소드가 실행된다. 이 메소드의 맨 마지막 this.webSecurity.build()
메소드가 실제 빈 객체 생성하고, 그 객체의 타입은 FilterChainProxy
이다. 해당 메소드를 계속 추적하면 아래와 같은 코드가 나온다.
실제 FilterChainProxy 인스턴스를 생성해주는 걸 확인할 수 있다.
여기까지 오면 일단 DelegatingFilterProxy
, springSecurityFilterChain
빈 객체
모두 생성이 된 것이다. 하지만 아직 연결은 안됐다.
연결은 맨 처음 DelegatingFilterProxy
에 요청이 왔을 때이다.
맨 처음 요청이 오면 if(delegateToUse == null)
분기문 내부로 들어간다.
그리고 이 과정에서 initDelegate
메소드가 수행된다.
이 메소드 내용을 보면 알겠지만 springSecurityFilterChain 이라는 이름의 빈 객체를 찾아서
위임 빈 객체로 지정한다.
이렇게 모든 초기화가 끝나면 그제서야 invokeDelegate
메소드를 실행하여
실제 springSecurityFilterChain
빈 객체에 요청을 위임하게 된다.
다시 말하지만 springSecurityFilterChain
는 FilterChainProxy
클래스 인스턴스다.
VirtualFilterChain
를 생성해서 해당 객체에 모든 요청 처리를 위임한다.
new VirtualFilterChain(filters)
의 인자값 filters
는 스프링 시큐리티의 보안 필터 목록들이다.
여기서부터 실제 보안 필터들이 수행된다. 끝!