回顾一下 Filter 过滤器
Spring Security的Servlet支持基于Servlet过滤器。
下图显示了单个HTTP请求处理程序的典型分层。
客户端向应用程序发送请求,容器创建一个FilterChain,其中包含 Filters 和Servlet,这些 Filters 和 Servlet 应根据请求URI的路径处理 HttpServletRequest。在Spring MVC应用程序中,Servlet 是 DispatcherServlet 的实例。最多一个 Servlet 可以处理单个 HttpServletRequest 和 HttpServletResponse 。但是,可以使用多个过滤器来:
防止调用下游 Filters 或 Servlet。在这种情况下,过滤器通常会写入 HttpServletResponse
修改下游 Filters 和 Servlet 使用的 HttpServletRequest 或 HttpServletResponse
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {// do something before the rest of the applicationchain.doFilter(request, response); // invoke the rest of the application// do something after the rest of the application}
由于 Filters 只影响下游 Filters 和 Servlet ,因此每个 Filter 的调用顺序非常重要。
DelegatingFilterProxy
Spring提供了一个名为DelegatingFilterProxy
的过滤器实现,它允许在 Servlet 容器的生命周期和 Spring 的 ApplicationContext 之间连接。 Servlet 容器允许使用自己的标准注册 Filters ,但它不知道 Spring bean 的定义。DelegatingFilterProxy
可以通过标准的 Servlet 容器注册机制,将所有工作委托给实现过滤器的 Spring Bean 。
下面是DelegatingFilterProxy
如何融入过滤器和过滤链的图片。
DelegatingFilterProxy
从 ApplicationContext 中查找 Bean Filter0 ,然后调用 Bean Filter0 。
大白话就是:他的任务就是把持有的 bean 从容器中拿出来。
DelegatingFilterProxy的伪代码如下所示。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {// Lazily get Filter that was registered as a Spring Bean// For the example in DelegatingFilterProxydelegateis an instance of Bean Filter0Filter delegate = getFilterBean(someBeanName);// delegate work to the Spring Beandelegate.doFilter(request, response);}
DelegatingFilterProxy
的另一个好处是,它允许延迟查找 Filter bean 实例。这一点很重要,因为容器需要在启动之前注册 Filter 实例。然而, Spring 通常使用ContextLoaderListener
来加载 SpringBean ,直到需要注册 Filter 实例之后才能进行加载。
而注入到 servletContext 的方式是通过DelegatingFilterProxyRegistrationBean
来完成,这个类的主要目的是为了包装DelegatingFilterProxy
类,并且实现自动注入到 servletContext 的流程
而DelegatingFilterProxy
可以获取到FilterChainProxy
这个bean,而这个 bean 注册SecurityFilterChain
和其中的安全过滤器。
最后使用SecurityFilterChain
来确定当前请求应该调用哪些Security Filter
所以DelegatingFilterProxyRegistrationBean
就是为了将SecurityFilterChain
加入servlet容器中。
而DelegatingFilterProxy
就是持有SecurityFilterChain
的 bean 名称的一个 Filter 。
@Overridepublic DelegatingFilterProxy getFilter() {return new DelegatingFilterProxy(this.targetBeanName, getWebApplicationContext()) {@Overrideprotected void initFilterBean() throws ServletException {// Don't initialize filter bean on init()}};}
FilterChainProxy
Spring Security 的 Servlet 支持包含在FilterChainProxy
中。FilterChainProxy
是 Spring Security 提供的一种特殊 Filter ,允许通过SecurityFilterChain
委托给许多 Filter 实例。由于FilterChainProxy
是一个 Bean ,它通常被包装在DelegatingFilterProxy
中。
也就是说DelegatingFilterProxy
可以获取到FilterChainProxy
这个bean
SecurityFilterChain
FilterChainProxy
使用SecurityFilterChain
来确定应该为此请求调用哪些 Spring 安全过滤器。
SecurityFilterChain
中的 Security Filter 通常是 bean ,但它们是用FilterChainProxy
注册的,而不是DelegatingFilterProxy
。FilterChainProxy
提供了许多直接注册 Servlet 容器或DelegatingFilterProxy
的优势。首先,它为 Spring Security 的所有 Servlet 支持提供了一个起点。因此,如果您试图解决 Spring Security 的 Servlet 支持问题,那么在FilterChainProxy
中添加调试点是一个很好的开始。
DelegatingFilterProxy
持有的是SecurityFilterChain
这个 spring 容器中的 bean 名称。
而FilterChainProxy
持有的是一个拦截器链集合 List 。
其次,由于FilterChainProxy
是 Spring Security 的核心,因此它可以执行不被视为可选的任务。例如,它可以清除 SecurityContext 以避免内存泄漏。它还可以应用 Spring Security 的 HttpFirewall 来保护应用程序免受某些类型的攻击。
此外,它在确定何时调用SecurityFilterChain
方面提供了更大的灵活性。在 Servlet 容器中,仅基于URL调用 Filter 。然而,FilterChainProxy
可以通过利用 RequestMatcher 接口根据 HttpServletRequest 中的任何内容确定调用。
事实上,FilterChainProxy
可以用来确定应该使用哪个SecurityFilterChain
。这允许为应用程序的不同部分提供完全独立的配置。
在多个SecurityFilterChain
的图中,FilterChainProxy
决定应使用哪个SecurityFilterChain
。将仅调用匹配的第一个SecurityFilterChain
。如果请求 /api/messages/ 的URL,它将首先匹配SecurityFilterChain0
的 /api/** 模式,因此仅调用SecurityFilterChain0
,即使它也匹配SecurityFilterChainn
。如果请求了 /messages/ 的URL,它将与SecurityFilterChain0
的 /api/** 模式不匹配,因此FilterChainProxy
将继续尝试每个SecurityFilterChain
。假设没有其他与SecurityFilterChain
匹配的SecurityFilterChainn
实例将被调用。
注意,SecurityFilterChain0
只配置了三个安全过滤器实例。然而,SecurityFilterChainn
配置了四个安全过滤器。需要注意的是,每个SecurityFilterChain
都可以是唯一的,并且可以单独配置。事实上,如果应用程序希望 Spring security 忽略某些请求,则SecurityFilterChain
可能没有安全过滤器。
FilterChainProxy
创建Spring Security Filter Chain
的过程
webSecurity.build()
@Configuration(proxyBeanMethods = false)public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {// .../*** Creates the Spring Security Filter Chain* @return the {@link Filter} that represents the security filter chain* @throws Exception*/@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)public Filter springSecurityFilterChain() throws Exception {boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();boolean hasFilterChain = !this.securityFilterChains.isEmpty();Assert.state(!(hasConfigurers && hasFilterChain),"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");if (!hasConfigurers && !hasFilterChain) {WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {});this.webSecurity.apply(adapter);}for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);for (Filter filter : securityFilterChain.getFilters()) {if (filter instanceof FilterSecurityInterceptor) {this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);break;}}}for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {customizer.customize(this.webSecurity);}return this.webSecurity.build();}// ...}
doBuild()
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {// ...@Overridepublic final O build() throws Exception {if (pareAndSet(false, true)) {this.object = doBuild();return this.object;}throw new AlreadyBuiltException("This object has already been built");}// ...}
performBuild()
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>extends AbstractSecurityBuilder<O> {// ...@Overrideprotected final O doBuild() throws Exception {synchronized (this.configurers) {this.buildState = BuildState.INITIALIZING;beforeInit();init();this.buildState = BuildState.CONFIGURING;beforeConfigure();configure();this.buildState = BuildState.BUILDING;O result = performBuild();this.buildState = BuildState.BUILT;return result;}}// ...}
创建 FilterChainProxy