该篇记录一下SecurityContextHolder与SecurityContext两个类,当然还有与它们关系密码的SecurityContextPersistenceFilter.java这个过滤器

1. SecurityContext.java

查看spring security的源码,发现它就是个接口,spring security提供了一个默认的实现SecurityContextImpl.java. 仔细一看,该类其实就是对Authentication对象进行了封装,当然,覆写了equals和hashCode两个方法。

public class SecurityContextImpl implements SecurityContext {

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    // ~ Instance fields
// ================================================================================================ private Authentication authentication; public SecurityContextImpl() {} public SecurityContextImpl(Authentication authentication) {
this.authentication = authentication;
} // ~ Methods
// ======================================================================================================== @Override
public boolean equals(Object obj) {
if (obj instanceof SecurityContextImpl) {
SecurityContextImpl test = (SecurityContextImpl) obj; if ((this.getAuthentication() == null) && (test.getAuthentication() == null)) {
return true;
} if ((this.getAuthentication() != null) && (test.getAuthentication() != null)
&& this.getAuthentication().equals(test.getAuthentication())) {
return true;
}
} return false;
} @Override
public Authentication getAuthentication() {
return authentication;
} @Override
public int hashCode() {
if (this.authentication == null) {
return -1;
}
else {
return this.authentication.hashCode();
}
} @Override
public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
} }

2. SecurityContextHolder.java

官方解释就是: Associates a given {@link SecurityContext} with the current execution thread.(与当前线程的securitycontext有关)

其实,它就是存储SecurityContext对象。

默认的策略采用ThreadLocal

源代码如下:

public class SecurityContextHolder {
// ~ Static fields/initializers
// ===================================================================================== public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
public static final String MODE_GLOBAL = "MODE_GLOBAL";
public static final String SYSTEM_PROPERTY = "spring.security.strategy";
private static String strategyName = System.getProperty(SYSTEM_PROPERTY);
private static SecurityContextHolderStrategy strategy;
private static int initializeCount = 0; static {
initialize();
} private static void initialize() {
// 如果没有设置自定义的策略,就采用MODE_THREADLOCAL模式
if (!StringUtils.hasText(strategyName)) {
// Set default
strategyName = MODE_THREADLOCAL;
}
// ThreadLocal策略
if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
}
// 采用InheritableThreadLocal,它是ThreadLocal的一个子类
else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
}
// 全局策略,实现方式就是static SecurityContext contextHolder
else if (strategyName.equals(MODE_GLOBAL)) {
strategy = new GlobalSecurityContextHolderStrategy();
}
else {
// 自定义的策略,通过返回创建出
try {
Class<?> clazz = Class.forName(strategyName);
Constructor<?> customStrategy = clazz.getConstructor();
strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
} initializeCount++;
} }

补充说明: InheritableThreadLocal 与 ThreadLocal的区别

ThreadLocal , 存储变量只能被当前线程使用

InheritableThreadLocal  , 父线程中存储的变量子线程也可使用

3. SecurityContextPersistenceFilter.java

该过滤器是spring security 过滤器链的第一个过滤器,所以请求进来时,第一个经过它,响应数据时,最后一个经过它。

请求进来时, 它会检测session中是否有SecurityContext,如果有,它会将SecurityContext从session中拿出来,放到线程中。

当请求响应时,它会检测线程是否有SecurityContext,如果有,它会将SecurityContext放到session中去。

这样,不同的请求,就可以拿到同一个认证信息 Authentication

下面看具体源码:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res; ...... // 将request与response对象封装成一个HttpRequestResponseHolder对象,减少方法列表个数
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,response); // 检测session中是否有SecurityContext,如果有就从session中获取,如果没有,创建一个新的
SecurityContext contextBeforeChainExecution = repo.loadContext(holder); try {
// 将SecurityContext对象放到当前执行的线程中
SecurityContextHolder.setContext(contextBeforeChainExecution);
// 调用过滤器链
chain.doFilter(holder.getRequest(), holder.getResponse()); }
finally {
// 从当前线程中获取SecurityContext对象
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
// 清空当前线程中的SecurityContext
SecurityContextHolder.clearContext();
// 将SecurityContext放入到session中
repo.saveContext(contextAfterChainExecution, holder.getRequest(),holder.getResponse());
}
}

接着看下loadContext(holder)方法的源码

public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
HttpServletRequest request = requestResponseHolder.getRequest();
HttpServletResponse response = requestResponseHolder.getResponse();
// 获取session
HttpSession httpSession = request.getSession(false);
// 从session中获取SecurityContext对象
SecurityContext context = readSecurityContextFromSession(httpSession); // 如果是null,就创建一个新的
if (context == null) {
context = generateNewContext(); }
....... return context;
}

4. 总结

这几个相关的类看完了,感叹spring的代码就是写得好!

(1) SecurityContextHolder它的责任就是存储SecurityContext对象,但是怎么存储,采用何种存储策略则是通过SecurityContextHolderStrategy来实现的。SecurityContextHolderStrategy只是一个抽象接口,spring security 默认提供了几种存储策略,它们都实现了SecurityContextHolderStrategy接口。如果我们想自定义存储策略,肯定也得实现SecurityContextHolderStrategy。这样子,SecurityContextHolder 只需要提供存储策略的方式,至于如何实现这种存储策略,则完全交给了SecurityContextHolderStrategy及其实现类来控制,做到责任分离吧!

(2) SecurityContextPersistenceFilter也是骚了一逼,将交量转换用得神了!

04 SecurityContextHolder与SecurityContext说明的更多相关文章

  1. 核心组件之SecurityContextHolder

    作用:保留系统当前的安全上下文细节,其中就包括当前使用系统的用户的信息.   上下文细节怎么表示?     用SecurityContext对象来表示   每个用户都会有它的上下文,那这个Securi ...

  2. spring security源码分析之web包分析

    Spring 是一个非常流行和成功的 Java 应用开发框架.Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案.一般来说,Web 应用的安全性包括 ...

  3. Spring Security(09)——Filter

    目录 1.1     Filter顺序 1.2     添加Filter到FilterChain 1.3     DelegatingFilterProxy 1.4     FilterChainPr ...

  4. Spring Security 入门(1-6-2)Spring Security - 内置的filter顺序、自定义filter、http元素和对应的filterChain

    Spring Security 的底层是通过一系列的 Filter 来管理的,每个 Filter 都有其自身的功能,而且各个 Filter 在功能上还有关联关系,所以它们的顺序也是非常重要的. 1.S ...

  5. Spring Security 架构与源码分析

    Spring Security 主要实现了Authentication(认证,解决who are you? ) 和 Access Control(访问控制,也就是what are you allowe ...

  6. Spring Security 过滤器链

    Alias Filter Class Namespace Element or Attribute CHANNEL_FILTER ChannelProcessingFilter http/interc ...

  7. Spring Security 5.0.x 参考手册 【翻译自官方GIT-2018.06.12】

    源码请移步至:https://github.com/aquariuspj/spring-security/tree/translator/docs/manual/src/docs/asciidoc 版 ...

  8. 如何使用Spring Security手动验证用户

    1.概述 在这篇快速文章中,我们将重点介绍如何以编程方式在Spring Security和Spring MVC中设置经过身份验证的用户. 2. Spring Security 简而言之,Spring ...

  9. Spring Security 梳理 - DelegatingFilterProxy

    可能你会觉得奇怪,我们在web应用中使用Spring Security时只在web.xml文件中定义了如下这样一个Filter,为什么你会说是一系列的Filter呢?    <filter> ...

随机推荐

  1. Jmeter 上传下载文件

    最近很多同学都在问jmeter上传.下载文件的脚本怎么做,要压测上传.下载文件的功能,脚本怎么做,网上查了都说的很含糊,这次呢,咱们就好好的把jmeter的上传下载文件好好缕缕,都整明白了,怎么个过程 ...

  2. 【CDN+】 Hive 入门 以及Handoop 系统认知

    前言 本文主要介绍Hive 的基础概念,以及Handoop的大体架构,组件依赖,对于大数据有个总体的认识 Hive 基础概念 官网:https://hive.apache.org/ The Apach ...

  3. Jmeter 循环控制器 遍历结果

    1.测试计划,添加Mysql jar包 2.线程组 3.JDBC Connection Configuration,配置Mysql 4.添加JDBC Request,将查询出的数据对应的存入usern ...

  4. vue-wacth监听事件

    2019-08-05   0:20 Vue.js 监听属性 watch,我们可以通过 watch 来响应数据的变化. 以下实例通过使用 watch 实现计数器:(此时我就想了一下,好像绑定点击事件,也 ...

  5. vue-安装及新建一个项目

    1.首先我们需要安装node.js,下载地址是:https://nodejs.org/en/ 之后是node.js的正常安装步骤: 接着打开window+R输入cmd回车进入命令行模块 2.确认nod ...

  6. Colourful Rectangle【扫描线】

    题目链接 很明显的可以发现是一个扫描线的问题,但是怎么处理区域呢,发现只有三种颜色,也就是最多也就是7种状态,那么我们可以进行一个状态压缩即可. 但是,在向上pushup的时候,存在我们要以子树的状态 ...

  7. jQuery easyUI 使用 datagrid 表格

    获取后台数据依旧是使用一般处理程序(ashx) ,分页上添加一个函数(pagerFilter(data)) 前端代码: <%@ Page Language="C#" Auto ...

  8. Windows程序设计--(三)窗口与消息

    3.1 窗口的创建 3.1.1 系统结构概述 所谓「Windows给程序发送消息」,是指Windows呼叫程序中的一个函数,该函数的参数描述了这个特定消息.这种位于Windows程序中的函数称为「窗口 ...

  9. springcloud费话之配置中心server修改

    目录: springcloud费话之Eureka基础 springcloud费话之Eureka集群 springcloud费话之Eureka服务访问(restTemplate) springcloud ...

  10. Vue PC端图片预览插件

    *手上的项目刚刚搞完了,记录一下项目中遇到的问题,留做笔记: 需求: 在项目中,需要展示用户上传的一些图片,我从后台接口拿到图片url后放在页面上展示,因为被图片我设置了宽度限制(150px),所以图 ...