3)Servlet 过滤器
Spring Security 过滤器链是一个非常复杂且灵活的引擎。Spring Security 的 Servlet 支持基于 Servlet 过滤器,因此通常首先了解过滤器的作用会很有帮助。 下图显示了单个 HTTP 请求的处理程序的典型分层。
客户端向应用程序发送请求,然后容器创建一个 FilterChain ,其中包含应根据请求URI的路径处理 HttpServletRequest 的过滤器和 Servlet。 在 Spring MVC 应用程序中,Servlet 是 DispatcherServlet 的实例。 一个 Servlet 最多只能处理一个 HttpServletRequest 和 HttpServletResponse 。 但是,可以使用多个过滤器来处理,比如:
-
阻止下游过滤器或 Servlet 被调用。 在这种情况下,过滤器通常用来写 HttpServletResponse
-
使用下游过滤器和 Servlet 来修改 HttpServletRequest或 HttpServletResponse
FilterChain 中 Filter s之间通过传递来发挥过滤器的作用。
1 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { 2 // do something before the rest of the application 3 chain.doFilter(request, response); // invoke the rest of the application 4 // do something after the rest of the application 5 }
注意,由于过滤器仅影响下游过滤器和 Servlet,因此调用每个过滤器的顺序非常重要。
下面来认识 Spring Security 中的几种类型的 Filter。
DelegatingFilterProxy
Spring 框架提供了一个名为 DelegatingFilterProxy 的 Filter 实现,该实现允许在 Servlet 容器的生命周期和 Spring 的 ApplicationContext 之间进行桥接。 顾名思义,它是一个代理过滤器,它充当过滤器,但它并未执行实际工作,而是将过滤委托给其他人。Servlet 容器允许符合自己的标准的 Filters 注册,但自己不了解 Spring 定义的 Bean, DelegatingFilterProxy 可以通过标准的 Servlet 容器机制来被注册,但是它将所有工作委托给实现了 Filter 的 Spring Bean。
以下是在 Spring Security 中添加 DelegatingFilterProxy 的方式,位置: AbstractSecurityWebApplicationInitializer#insertSpringSecurityFilterChain()
1 /** 2 * Registers the springSecurityFilterChain 3 * @param servletContext the {@link ServletContext} 4 */ 5 private void insertSpringSecurityFilterChain(ServletContext servletContext) { 6 String filterName = DEFAULT_FILTER_NAME;// This is a spring name which is called "springSecurityFilterChain" 7 DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy( 8 filterName); 9 String contextAttribute = getWebApplicationContextAttribute(); 10 if (contextAttribute != null) { 11 springSecurityFilterChain.setContextAttribute(contextAttribute); 12 } 13 registerFilter(servletContext, true, filterName, springSecurityFilterChain); 14 }
名为 springSecurityFilterChain 的 Bean 的创建,位置:WebSecurityConfiguration#springSecurityFilterChain()
1 /** 2 * Creates the Spring Security Filter Chain 3 * @return the {@link Filter} that represents the security filter chain 4 * @throws Exception 5 */ 6 @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) 7 public Filter springSecurityFilterChain() throws Exception { 8 boolean hasConfigurers = webSecurityConfigurers != null 9 && !webSecurityConfigurers.isEmpty(); 10 if (!hasConfigurers) { 11 WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor 12 .postProcess(new WebSecurityConfigurerAdapter() { 13 }); 14 webSecurity.apply(adapter); 15 } 16 return webSecurity.build(); 17 }
下面是 DelegatingFilterProxy 如何传递 Filters 和 FilterChain 的图。
DelegatingFilterProxy
1 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { 2 // Lazily get Filter that was registered as a Spring Bean 3 // For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0 4 Filter delegate = getFilterBean(someBeanName); 5 // delegate work to the Spring Bean 6 delegate.doFilter(request, response); 7 }
一旦 Servlet 请求到达过滤器,DelegatingFilterProxy 就会被初始化,作为其初始化的一部分,它会寻找过滤器名称,在上面的示例中,过滤器名称为 springSecurityFilterChain 。
DelegatingFilterProxy 的另一个好处是,它允许延迟查找 Filter bean 实例。 这很重要,因为容器需要在容器启动之前注册 Filter 实例。 但是,Spring 通常使用 ContextLoaderListener来加载 Spring Bean,但需要等到注册完 Filter 实例之后,Spring 才会完成加载这一操作。
ContextLoaderListener是引导侦听器。它启动和关闭 Spring 的 Root WebApplicationContext。
ContextLoaderListener是可选的,你可以仅配置 Dispatcher Servlet 来运行 Spring 应用程序,但是对于 Spring Security,则需要一个。
DispatcherServlet的上下文专用于与 MVC 相关的 Bean,例如控制器,视图,处理程序。它不包括与安全性相关的 bean,要使用 Spring Security,必须将与安全性相关的 bean 放在Root WebApplicationContext 中,因此需要配置 ContextLoaderListener。
FilterChainProxy
Spring Security 的 Servlet 支持包含在 FilterChainProxy 中。 FilterChainProxy是 Spring Security 提供的一个特殊过滤器,允许通过SecurityFilterChain委派许多过滤器实例。 由于FilterChainProxy是 Bean,因此通常将其包装在 DelegatingFilterProxy 中。
其次,由于 FilterChainProxy 对于 Spring Security 的使用至关重要,因此它可以执行不被视为可选的任务。 例如,它清除 SecurityContext 以避免内存泄漏。 它还使用 Spring Security 的 HttpFirewall 来保护应用程序免受某些类型的攻击。
此外,它在确定何时应调用 SecurityFilterChain
查看springSecurityFilterChain的方法
通过注解获取已注册的 springSecurityFilterChain 的方法
1 @Autowired 2 @Qualifier("springSecurityFilterChain") 3 private Filter springSecurityFilterChain;
接下来需要将此对象转换为 FilterChainProxy 并调用 getFilterChains() 方法:
1 public void getFilters() { 2 FilterChainProxy filterChainProxy = (FilterChainProxy) springSecurityFilterChain; 3 List<SecurityFilterChain> list = filterChainProxy.getFilterChains(); 4 list.stream() 5 .flatMap(chain -> chain.getFilters().stream()) 6 .forEach(filter -> System.out.println(filter.getClass())); 7 }
在自己的 WebSecurityConfig (Spring Security)中启用安全性调试,该调试将记录每个请求的详细安全性信息。我们可以使用 debug 属性启用安全调试:
1 @EnableWebSecurity(debug = true)
这样,当我们向服务器发送请求时,所有请求信息都会被记录下来。我们还将能够看到整个默认的 Spring Security 过滤器链:
1 Security filter chain: [ 2 WebAsyncManagerIntegrationFilter 3 SecurityContextPersistenceFilter 4 HeaderWriterFilter 5 CsrfFilter 6 LogoutFilter 7 UsernamePasswordAuthenticationFilter 8 DefaultLoginPageGeneratingFilter 9 DefaultLogoutPageGeneratingFilter 10 RequestCacheAwareFilter 11 SecurityContextHolderAwareRequestFilter 12 AnonymousAuthenticationFilter 13 SessionManagementFilter 14 15 ExceptionTranslationFilter 16 VBFilterSecurityInterceptor 17 FilterSecurityInterceptor 18 ]
最后,让我们看一些重要的安全过滤器:
-
SecurityContextPersistenceFilter :从 JSESSIONID 恢复身份验证
-
UsernamePasswordAuthenticationFilter :执行身份验证,默认情况下响应“ /login” URL
-
AnonymousAuthenticationFilter :当 SecurityContextHolder 中没有身份验证对象时,它将创建一个匿名身份验证对象并将其放置在此处
-
FilterSecurityInterceptor :拒绝访问时引发异常,可能会抛出身份验证和授权异常
-
ExceptionTranslationFilter :从 FilterSecurityInterceptor 捕获安全异常
1 @Override 2 protected void configure(HttpSecurity http) throws Exception { 3 http.addFilterBefore(vbFilterSecurityInterceptor, FilterSecurityInterceptor.class); 4 }
参考:Spring Security 官方文档,DelegatingFilterProxy ,springSecurityFilterChain ,ContextLoaderListener and FilterChainProxy,How Spring Security Filter Chain works,Overview and Need for DelegatingFilterProxy in Spring