写在前面

上回我们讲了spring security整合spring springmvc的流程,并且知道了spring security是通过过滤器链来进行认证授权操作的。今天我们来分析一下springsecurity过滤器链的加载流程。读者在阅读本文时可以边阅读边跟着操作,这样子会理解的更清楚一些。

Spring Security过滤器链

spring security的过滤器非常多,这里简单介绍几个常用的过滤器。

spring security常过滤器链介绍

org.springframework.security.web.context.SecurityContextPersistenceFilter

主要是使用SecurityContextRepository在session中保存或更新一个SecurityContext域对象(相当于一个容器),并将SecurityContext给以后的过滤器使用,来为后续filter建立所需的上下文。SecurityContext中存储了当前用户的认证以及权限信息。 其他的过滤器都需要依赖于它。在 Spring Security 中,虽然安全上下文信息被存储于 Session 中,但我们在实际使用中不应该直接操作 Session,而应当使用 SecurityContextHolder。

org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter

这个过滤器用于集成SecurityContext到Spring异步执行机制的WebAsyncManager 中。如果想要与spring集成,就必须要使用此过滤器链。

org.springframework.security.web.csrf.CsrfFilter

csrf又称跨域请求伪造,SpringSecurity会对所有post请求验证是否包含系统生成的csrf的token信息,如果不包含,则报错。起到防止csrf攻击的效果。

题外话:csrf攻击

CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。

一个例子:

比如你登陆了银行网站A之后,浏览器cookie中将保存你的登录信息。同时你没有没有注销登录的情况下又用同一浏览器访问了视频网站B,假设视频网站B中含有csrf病毒,其将获取你的cookie内容,拿到你银行网站A的登录信息,就可以进行不可描述之操作了。

而这个CsrfFilter通过签发token的方式进行访问验证,如果token不是本网站签发的或者访问请求中不带有这个token则拒绝访问。

org.springframework.security.web.authentication.logout.LogoutFilter

匹配URL为/logout的请求,实现用户退出,清除认证信息。

org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

认证操作全靠这个过滤器,默认匹配URL为/login且必须为POST请求。

org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter

如果没有在配置文件中指定认证页面,则由该过滤器生成一个默认认证页面

org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter

由此过滤器可以生产一个默认的退出登录页面

org.springframework.security.web.authentication.www.BasicAuthenticationFilter

此过滤器会自动解析HTTP请求中头部名字为Authentication,且以Basic开头的头信息。

org.springframework.security.web.savedrequest.RequestCacheAwareFilter

通过HttpSessionRequestCache内部维护了一个RequestCache,用于缓存HttpServletRequest

org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter

针对ServletRequest进行了一次包装,使得request具有更加丰富的API

org.springframework.security.web.authentication.AnonymousAuthenticationFilter

当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到SecurityContextHolder中。

spring security为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份(游客)。

org.springframework.security.web.session.SessionManagementFilter

SecurityContextRepository限制同一用户开启多个会话的数量

org.springframework.security.web.access.ExceptionTranslationFilter

异常转换过滤器位于整个springSecurityFilterChain的后方,用来转换整个链路中出现的异常

org.springframework.security.web.access.intercept.FilterSecurityInterceptor

获取所配置资源访问的授权信息,根据SecurityContextHolder中存储的用户信息来决定其是否有权限。

spring security加载流程

在web.xml中,我们配置了一个名字为“springSecurityFilterChain”的过滤器org.springframework.web.filter.DelegatingFilterProxy。以下是部分源码。

public class DelegatingFilterProxy extends GenericFilterBean {
@Nullable
private String contextAttribute;
@Nullable
private WebApplicationContext webApplicationContext;
@Nullable
private String targetBeanName;
private boolean targetFilterLifecycle;
/** 真正加载的过滤器*/
@Nullable
private volatile Filter delegate;
private final Object delegateMonitor; protected void initFilterBean() throws ServletException {
synchronized(this.delegateMonitor) {
if (this.delegate == null) {
if (this.targetBeanName == null) {
this.targetBeanName = this.getFilterName();
} WebApplicationContext wac = this.findWebApplicationContext();
if (wac != null) {
this.delegate = this.initDelegate(wac);
}
} }
}
/** 过滤器的入口 */
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized(this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = this.findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
}
/** 在做完一系列判断之后,真正要做的就是这一步,初始化delegate*/
delegateToUse = this.initDelegate(wac);
}
this.delegate = delegateToUse;
}
} this.invokeDelegate(delegateToUse, request, response, filterChain);
} protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
String targetBeanName = this.getTargetBeanName();
Assert.state(targetBeanName != null, "No target bean name set");
Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class);
if (this.isTargetFilterLifecycle()) {
delegate.init(this.getFilterConfig());
} return delegate;
} protected void invokeDelegate(Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
} }

接下来我们在initDelegate方法上打个断点调试一下



最终我们发现,通过initDelegate方法给delegate初始化,得到一个FilterChainProxy对象。接下来我们进入FilterChainProxy这个类看看

public class FilterChainProxy extends GenericFilterBean {
。。。。
}

可以发现这也是一个过滤器,那么我们找dofilter方法。



接下来我们在doFilterInternal里面打个断点试试,看看能不能得到过滤器链

我们在这一步得到了一个List,点开可以发现正好是前文讲到的一些常用的过滤器。

我们再进入到getFilters中看看这些过滤器链在哪

private List<Filter> getFilters(HttpServletRequest request) {
Iterator var2 = this.filterChains.iterator(); SecurityFilterChain chain;
do {
if (!var2.hasNext()) {
return null;
} chain = (SecurityFilterChain)var2.next();
} while(!chain.matches(request)); return chain.getFilters();
}

从该方法我们知道过滤器链来自于SecurityFilterChain的getFilters方法,接下来,我们看看这个类

public interface SecurityFilterChain {
boolean matches(HttpServletRequest var1); List<Filter> getFilters();
}

可以发现这是一个接口,在idea中ctrl+H可以发现它只有一个实现类,那就是DefaultSecurityFilterChain

到这一步,我们可以发现,springSecurity的过滤器链存在于SecurityFilterChain中(springSecurity的过滤器链由SecurityFilterChain负责封装),它只有一个实现类那就是DefaultSecurityFilterChain。也就是说其过滤器链由SecurityFilterChain封装。

至此,springSecurity的过滤器链加载流程我们就说完了。

Spring Security拦截器加载流程分析--练气中期的更多相关文章

  1. 0002 - Spring MVC 拦截器源码简析:拦截器加载与执行

    1.概述 Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理.例如通过拦截器可以进行权限验证.记录请求信息的日 ...

  2. interface21 - web - ContextLoaderListener(Spring Web Application Context加载流程)

    前言 最近打算花点时间好好看看spring的源码,然而现在Spring的源码经过迭代的版本太多了,比较庞大,看起来比较累,所以准备从最初的版本(interface21)开始入手,仅用于学习,理解其设计 ...

  3. Spring Security认证流程分析--练气后期

    写在前面 在前一篇文章中,我们介绍了如何配置spring security的自定义认证页面,以及前后端分离场景下如何获取spring security的CSRF Token.在这一篇文章中我们将来分析 ...

  4. Spring源码剖析2:Spring IOC容器的加载过程

    spring ioc 容器的加载流程 1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:In ...

  5. Spring源码剖析3:Spring IOC容器的加载过程

    本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...

  6. 解决Spring Boot 拦截器注入service为空的问题

    问题:在自定义拦截器中,使用了@Autowaire注解注入了封装JPA方法的Service,结果发现无法注入,注入的service为空 0.原因分析 拦截器加载的时间点在springcontext之前 ...

  7. Spring Security自定义认证页面(动态网页解决方案+静态网页解决方案)--练气中期圆满

    写在前面 上一回我们简单分析了spring security拦截器链的加载流程,我们还有一些简单的问题没有解决.如何自定义登录页面?如何通过数据库获取用户权限信息? 今天主要解决如何配置自定义认证页面 ...

  8. springBoot整合spring security+JWT实现单点登录与权限管理--筑基中期

    写在前面 在前一篇文章当中,我们介绍了springBoot整合spring security单体应用版,在这篇文章当中,我将介绍springBoot整合spring secury+JWT实现单点登录与 ...

  9. java面试记录二:spring加载流程、springmvc请求流程、spring事务失效、synchronized和volatile、JMM和JVM模型、二分查找的实现、垃圾收集器、控制台顺序打印ABC的三种线程实现

    注:部分答案引用网络文章 简答题 1.Spring项目启动后的加载流程 (1)使用spring框架的web项目,在tomcat下,是根据web.xml来启动的.web.xml中负责配置启动spring ...

随机推荐

  1. Day08_商品规格管理

    学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"乐优商城"获取视频和教程资料! b站在线视频 0.学习 ...

  2. 零起点PYTHON机器学习快速入门 PDF |网盘链接下载|

      点击此处进入下载地址 提取码:2wg3 资料简介: 本书采用独创的黑箱模式,MBA案例教学机制,结合一线实战案例,介绍Sklearn人工智能模块库和常用的机器学习算法.书中配备大量图表说明,没有枯 ...

  3. Numpy创建数组

    # 导入numpy 并赋予别名 np import numpy as np # 创建数组的常用的几种方式(列表,元组,range,arange,linspace(创建的是等差数组),zeros(全为 ...

  4. Python os.tcgetpgrp() 方法

    概述 os.tcgetpgrp() 方法用于回与终端fd(一个由os.open()返回的打开的文件描述符)关联的进程组.高佣联盟 www.cgewang.com 语法 tcgetpgrp()方法语法格 ...

  5. Python os.fchown() 方法

    概述 os.fchown() 方法用于修改一个文件的所有权,这个函数修改一个文件的用户ID和用户组ID,该文件由文件描述符fd指定.高佣联盟 www.cgewang.com Unix上可用. 语法 f ...

  6. HTML - XHTML

    HTML - XHTML XHTML 是以 XML 格式编写的 HTML.高佣联盟 www.cgewang.com 什么是 XHTML? XHTML 指的是可扩展超文本标记语言 XHTML 与 HTM ...

  7. PHP substr_count() 函数

    实例 计算 "world" 在字符串中出现的次数: <?php高佣联盟 www.cgewang.comecho substr_count("Hello world. ...

  8. luogu P4525 自适应辛普森法1

    LINK:自适应辛普森法1 观察题目 这个东西 凭借我们的数学知识应该是化简不了的. 可以直接认为是一个函数 求定积分直接使用辛普森就行辣. 一种写法: double a,b,c,d; double ...

  9. day2. 六大基本数据类型简介

    一.基本数据类型 Number 数字类型 (int float bool complex) str 字符串类型 list 列表类型 tuple 元组类型 set 集合类型 dict 字典类型 二.Nu ...

  10. 【USACO02FEB】Rebuilding Roads 重建道路 题解(树形DP)

    题目链接 题目大意:问使含有$p$个节点的子树分离至少需要去掉几条边. ------------------ 设$f[i][j]$表示以$i$为根的子树保留$j$个节点所去掉的最少边数. 初始化$f[ ...