Spring Security:Servlet 过滤器(三)
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之间通过传递来发挥过滤器的作用。
Filter
的用法例子:
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 从 ApplicationContext 查找 Bean Filter0 ,然后调用 Bean Filter0 。 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 中。
SecurityFilterChain
FilterChainProxy 使用 SecurityFilterChain 确定应对此请求调用哪些 Spring Security 过滤器。
SecurityFilterChain
中的安全过滤器通常是 Bean,但它们是使用 FilterChainProxy 而不是 DelegatingFilterProxy 来注册的。 FilterChainProxy 具有直接向 Servlet 容器或 DelegatingFilterProxy 注册的许多优点。 首先,它为 Spring Security 的所有 Servlet 支持提供了一个起点。 因此,如果您想对Spring Security的 Servlet 支持进行故障排除,那么在 FilterChainProxy 中添加调试点是一个很好的起点。
其次,由于 FilterChainProxy 对于 Spring Security 的使用至关重要,因此它可以执行不被视为可选的任务。 例如,它清除 SecurityContext 以避免内存泄漏。 它还使用 Spring Security 的 HttpFirewall 来保护应用程序免受某些类型的攻击。
此外,它在确定何时应调用 SecurityFilterChain 时提供了更大的灵活性。 在Servlet容器中,仅根据URL调用过滤器。 但是, FilterChainProxy 可以利用 RequestMatcher 接口,根据 HttpServletRequest 中的任何内容确定调用。
实际上, FilterChainProxy 可用于确定应使用哪个 SecurityFilterChain 。 如果你的应用程序在各种不同的方面提供了相对独立的配置。
查看springSecurityFilterChain的方法
通过注解获取已注册的 springSecurityFilterChain 的方法
1 @Autowired
2 @Qualifier("springSecurityFilterChain")
3 private Filter springSecurityFilterChain;
在这里,使用名称为 springSecurityFilterChain 的 @Qualifier ,其类型为 Filter 而不是 FilterChainProxy 。这是因为在 WebSecurityConfiguration 中的方法 springSecurityFilterChain() ,它创建 Spring Security 的过滤器链,返回类型 Filter ,而不是 FilterChainProxy 。
接下来需要将此对象转换为 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 捕获安全异常
其中 VBFilterSecurityInterceptor 是自定义的验证过滤器,继承了 AbstractSecurityInterceptor 并实现了 javax.servlet.Filter 接口,可以在你的 Spring Security 配置 Bean中的 configure() 方法中通过 http.addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter) 来配置:
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
Spring Security:Servlet 过滤器(三)的更多相关文章
- Spring Security入门(1-12)Spring Security 的过滤器机制
Servlet过滤器被用来拦截用户请求来进行请求之前或之后的处理,或者干脆重定向这个请求,这取决于servlet过滤器的功能. Servlet过滤器处理之后的目标servlet是 MVC 分发web ...
- spring security 图解过滤器的使用
1. HttpSessionContextIntegrationFilter 位于过滤器顶端,第一个起作用的过滤器. 用途一,在执行其他过滤器之前,率先判断用户的session中是否已经存在一个Sec ...
- Re:从零开始的Spring Security Oauth2(三)
上一篇文章中我们介绍了获取token的流程,这一篇重点分析一下,携带token访问受限资源时,内部的工作流程. @EnableResourceServer与@EnableAuthorizationSe ...
- Spring Security认证配置(三)
学习本章之前,可以先了解下上篇Spring Security认证配置(二) 本篇想要达到这样几个目的: 1.登录成功处理 2.登录失败处理 3.调用方自定义登录后处理类型 具体配置代码如下: spri ...
- spring security基本知识(三) 过滤详细说明
在我们前面的文章Spring Security 初识(一)中,我们看到了一个最简单的 Spring Security 配置,会要求所有的请求都要经过认证.但是,这并不是我们想要的,我们通常想自定义应用 ...
- spring-security-4 (3)spring security过滤器的创建与注册原理
spring security是通过一个过滤器链来保护你的web应用安全.在spring security中,该过滤链的名称为springSecurityFilterChain,类型为FilterCh ...
- Spring Security(2):过滤器链(filter chain)的介绍
上一节中,主要讲了Spring Security认证和授权的核心组件及核心方法.但是,什么时候调用这些方法呢?答案就是Filter和AOP.Spring Security在我们进行用户认证以及授予权限 ...
- Spring Security(三十一):9.6 Localization(本地化)
Spring Security supports localization of exception messages that end users are likely to see. If you ...
- Spring Security(四) —— 核心过滤器源码分析
摘要: 原创出处 https://www.cnkirito.moe/spring-security-4/ 「老徐」欢迎转载,保留摘要,谢谢! 4 过滤器详解 前面的部分,我们关注了Spring Sec ...
随机推荐
- 路由懒加载---Vue Router
一.什么是懒加载? 懒加载也就是延迟加载或者按需加载,即在需要的时候进行加载. 二.为什么在Vue路由中使用懒加载? 像vue这种单页面应用,如果没有应用懒加载,运用webpack打包后的文件将会异常 ...
- pythonGUI-PySide2的使用笔记
用python开发跨平台的图形化界面,主流的有3种选择: Tkinter 基于Tk的Python库,Python官方标准库,稳定.发布程序较小,缺点是控件相对较少. wxPython 基于wxWidg ...
- DEDE整合套件实现本地多个网站随意切换的开发环境
一.修改WEB全局配置: 在Listen 80 后面添加自己的端口号. 例如,2020是我的端口 Listen 2020 二.修改WEB站点配置: a---在NameVirtualHost *:80后 ...
- Shell系列(32)- 双分支if语句判断Apache服务是否启动
#!/bin/bash #截取httped进程,并把结果赋予变量test test=$(ps -aux | grep "httpd" | grep -v "grep&qu ...
- Matlab使用随记
Matlab 2020 想要看图像每一点的值大小 工具--->数据提示 想要导出的分辨率提高 导出设置--->渲染--->600dpi Matlab 2017b 程序运行后,画出图, ...
- P4590-[TJOI2018]游园会【dp套dp】
正题 题目链接:https://www.luogu.com.cn/problem/P4590 题目大意 给出一个长度为\(m\)的字符串\(s\). 对于每个\(k\in[0,m]\)求有多少个长度为 ...
- P4345-[SHOI2015]超能粒子炮·改【Lucas定理,类欧】
正题 题目链接:https://www.luogu.com.cn/problem/P4345 题目大意 \(T\)组询问,给出\(n,k\)求 \[\sum_{i=0}^{k}\binom{n}{i} ...
- span标签的巧用
前言: 用struts的标签结合<td>标签将后台数据在表格里渲染了出来,目前需求是需要将两个状态组合在一起显示 解决:通过span标签在td里的套用可以实现 <logic:notE ...
- FTP和TFTP
文件传输协议 FTP概述: 文件传输协议FTP(File Transfer Protocol)[RFC 959]是互联网上使用最广泛的文件传输协议, FTP提供交互式的访问,允许用户知指明文件类型与格 ...
- 10.6.2 sendfile
1.传统Linux中 I/O 的问题 2.传统的 Linux 系统的标准 I/O 接口( read. write)是基于数据拷贝的,也就是数据都是 copy_to_user 或者 copy_from_ ...