SpringSecurity 核心过滤器——SecurityContextPersistenceFilter
文章目录
- 前言
- 过滤器介绍
- 用户信息的存储
- 获取用户信息
- 存储用户信息
- 获取用户信息
- 处理逻辑
- 总结
前言
SecurityContextHolder,这个是一个非常基础的对象,存储了当前应用的上下文SecurityContext,而在SecurityContext可以获取Authentication对象。也就是当前认证的相关信息会存储在Authentication对象中。

默认情况下,SecurityContextHolder是通过 ThreadLocal来存储对应的信息的。也就是在一个线程中我们可以通过这种方式来获取当前登录的用户的相关信息。而在SecurityContext中就只提供了对Authentication对象操作的方法。
在项目中可以通过以下方式获取当前登录的用户信息
public String hello(){Authentication authentication = SecurityContextHolder.getContext().getAuthentication();Object principal = authentication.getPrincipal();if(principal instanceof UserDetails){UserDetails userDetails = (UserDetails) principal;System.out.println(userDetails.getUsername());return "当前登录的账号是:" + userDetails.getUsername();}return "当前登录的账号-->" + principal.toString();}
调用 getContext()返回的对象是 SecurityContext接口的一个实例,这个对象就是保存在线程中的。接下来将看到,Spring Security中的认证大都返回一个 UserDetails的实例作为principa。
过滤器介绍
在Session中维护一个用户的安全信息就是这个过滤器处理的。从request中获取session,从Session中取出已认证用户的信息保存在SecurityContext中,提高效率,避免每一次请求都要解析用户认证信息,方便接下来的filter直接获取当前的用户信息。
用户信息的存储
用户信息的存储是通过SecutiryContextRepository接口操作的,定义了对SecurityContext的存储操作,在该接口中定义了如下的几个方法
public interface SecurityContextRepository {/*** 获取SecurityContext对象*/SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);/*** 存储SecurityContext*/void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response);/*** 判断是否存在SecurityContext对象*/boolean containsContext(HttpServletRequest request);}
默认的实现是HttpSessionSecurityContextRepository。也就是把SecurityContext存储在了HttpSession中。对应的抽象方法实现如下:
获取用户信息
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {// 获取对有的Request和Response对象HttpServletRequest request = requestResponseHolder.getRequest();HttpServletResponse response = requestResponseHolder.getResponse();// 获取HttpSession对象HttpSession httpSession = request.getSession(false);// 从HttpSession中获取SecurityContext对象SecurityContext context = readSecurityContextFromSession(httpSession);if (context == null) {// 如果HttpSession中不存在SecurityContext对象就创建一个// SecurityContextHolder.createEmptyContext();// 默认是ThreadLocalSecurityContextHolderStrategy存储在本地线程中context = generateNewContext();if (this.logger.isTraceEnabled()) {this.logger.trace(LogMessage.format("Created %s", context));}}// 包装Request和Response对象SaveToSessionResponseWrapper wrappedResponse = new SaveToSessionResponseWrapper(response, request,httpSession != null, context);requestResponseHolder.setResponse(wrappedResponse);requestResponseHolder.setRequest(new SaveToSessionRequestWrapper(request, wrappedResponse));return context;}
存储用户信息
@Overridepublic void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = WebUtils.getNativeResponse(response,SaveContextOnUpdateOrErrorResponseWrapper.class);Assert.state(responseWrapper != null, () -> "Cannot invoke saveContext on response " + response+ ". You must use the HttpRequestResponseHolder.response after invoking loadContext");responseWrapper.saveContext(context);}
@Overrideprotected void saveContext(SecurityContext context) {// 获取Authentication对象final Authentication authentication = context.getAuthentication();// 获取HttpSession对象HttpSession httpSession = this.request.getSession(false);// String springSecurityContextKey = HttpSessionSecurityContextRepository.this.springSecurityContextKey;// See SEC-776if (authentication == null|| HttpSessionSecurityContextRepository.this.trustResolver.isAnonymous(authentication)) {if (httpSession != null && this.authBeforeExecution != null) {// SEC-1587 A non-anonymous context may still be in the session// SEC-1735 remove if the contextBeforeExecution was not anonymoushttpSession.removeAttribute(springSecurityContextKey);this.isSaveContextInvoked = true;}if (this.logger.isDebugEnabled()) {if (authentication == null) {this.logger.debug("Did not store empty SecurityContext");}else {this.logger.debug("Did not store anonymous SecurityContext");}}return;}httpSession = (httpSession != null) ? httpSession : createNewSessionIfAllowed(context, authentication);// If HttpSession exists, store current SecurityContext but only if it has// actually changed in this thread (see SEC-37, SEC-1307, SEC-1528)if (httpSession != null) {// We may have a new session, so check also whether the context attribute// is set SEC-1561if (contextChanged(context) || httpSession.getAttribute(springSecurityContextKey) == null) {// HttpSession 中存储SecurityContexthttpSession.setAttribute(springSecurityContextKey, context);this.isSaveContextInvoked = true;if (this.logger.isDebugEnabled()) {this.logger.debug(LogMessage.format("Stored %s to HttpSession [%s]", context, httpSession));}}}}
获取用户信息
@Overridepublic boolean containsContext(HttpServletRequest request) {// 获取HttpSessionHttpSession session = request.getSession(false);if (session == null) {return false;}// 从session中能获取就返回true否则falsereturn session.getAttribute(this.springSecurityContextKey) != null;}
处理逻辑
在请求到达时,SecurityContextPersistenceFilter会从HTTP请求中读取用户凭证,并使用这些信息来恢复用户的安全上下文。在请求完成后,它会在HTTP响应中写入用户凭证,以便在下一次请求时可以恢复用户的安全上下文。具体处理逻辑:
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {// 同一个请求之处理一次if (request.getAttribute(FILTER_APPLIED) != null) {chain.doFilter(request, response);return;}// 更新状态request.setAttribute(FILTER_APPLIED, Boolean.TRUE);// 是否提前创建 HttpSessionif (this.forceEagerSessionCreation) {// 创建HttpSessionHttpSession session = request.getSession();if (this.logger.isDebugEnabled() && session.isNew()) {this.logger.debug(LogMessage.format("Created session %s eagerly", session.getId()));}}// 把Request和Response对象封装为HttpRequestResponseHolder对象HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);// 获取SecurityContext对象SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);try {// SecurityContextHolder绑定SecurityContext对象SecurityContextHolder.setContext(contextBeforeChainExecution);if (contextBeforeChainExecution.getAuthentication() == null) {logger.debug("Set SecurityContextHolder to empty SecurityContext");}else {if (this.logger.isDebugEnabled()) {this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", contextBeforeChainExecution));}}// 结束交给下一个过滤器处理chain.doFilter(holder.getRequest(), holder.getResponse());}finally {// 当其他过滤器都处理完成后SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();// 移除SecurityContextHolder中的SecuritySecurityContextHolder.clearContext();// 把this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());// 存储Context在HttpSession中request.removeAttribute(FILTER_APPLIED);this.logger.debug("Cleared SecurityContextHolder to complete request");}}
通过上面的代码逻辑其实就清楚了在SpringSecurity中的认证信息的流转方式了。首先用户的认证状态Authentication是存储在SecurityContext中的,而每个用户的SecurityContext是统一存储在HttpSession中的。一次请求流转中我们需要获取当前的认证信息是通过SecurityContextHolder来获取的,默认是在ThreadLocal中存储的。
总结
SecurityContextPersistenceFilter是Spring Security框架中一个重要的过滤器,用于处理与用户安全上下文相关的操作,通常位于Spring Security过滤器链的最前面,因为需要在其他过滤器执行之前恢复用户的安全上下文。在Spring Security的过滤器链中,SecurityContextPersistenceFilter之后的过滤器可能会改变用户的身份或安全上下文,例如AuthenticationFilter可能会对用户的身份进行认证。
相关文章:
SpringSecurity 核心过滤器——SecurityContextPersistenceFilter
文章目录 前言过滤器介绍用户信息的存储获取用户信息存储用户信息获取用户信息 处理逻辑总结 前言 SecurityContextHolder,这个是一个非常基础的对象,存储了当前应用的上下文SecurityContext,而在SecurityContext可以获取Authentication对象…...
反转单链表
思路图1: 代码: struct ListNode* reverseList(struct ListNode* head){if(headNULL)//当head是空链表时 {return head; }struct ListNode* n1NULL;struct ListNode* n2head;struct ListNode* n3head->next;if(head->nextNULL)//当链表只有一个节…...
加速新药问世,药企如何利用云+网的优势?
随着计算能力的不断提高和人工智能技术的迅速发展,药物研发领域正迎来一场革命。云端强大的智能算法正成为药物研发企业的得力助手,推动着药物的精确设计和固相筛选。这使得药物设计、固相筛选以及药物制剂开发的时间大幅缩短,有望加速新药物…...
C++中string对象之间比较、char*之间比较
#include <cstring> //char* 使用strcmp #include <string> //string 使用compare #include <iostream> using namespace std; int main() {string stringStr1 "42";string stringStr2 "42";string stringStr3 "213";cout …...
MVVM模式理解
链接: MVVM框架理解及其原理实现 - 知乎 (zhihu.com) 重点: 1.将展示的界面窗口和创建的构件是数据进行分离 2.利用一个中间商进行数据的处理,所有的数据通过中间商进行处理...
js常用的数组处理方法
some 方法 用于检查数组中是否至少有一个元素满足指定条件。如果有满足条件的元素,返回值为 true,否则返回 false。 const numbers [1, 2, 3, 4, 5];const hasEvenNumber numbers.some((number) > number % 2 0); console.log(hasEvenNumber); /…...
[Document]VectoreStoreToDocument开发
该document是用来检索文档的。 第一步:定义组件对象,该组件返回有两种类型:document和text。 第二步:获取需要的信息,向量存储库,这里我使用的是内存向量存储(用该组件拿到文档,并检…...
【LeetCode-简单题】225. 用队列实现栈
文章目录 题目方法一:单个队列实现 题目 方法一:单个队列实现 入栈 和入队正常进行出栈的元素其实就是队列的尾部元素,所以直接将尾部元素弹出即可,其实就可以将除了最后一个元素的其他元素出队再加入队,然后弹出队首元…...
数据预处理方式合集
删除空行 #del all None value data_all.dropna(axis1, howall, inplaceTrue) 删除空列 #del all None value data_all.dropna(axis0, howall, inplaceTrue) 缺失值处理 观测缺失值 观测数据缺失值有一个比较好用的工具包——missingno,直接传入DataFrame&…...
【前端】jquery获取data-*的属性值
通过jquery获取下面data-id的值 <div id"getId" data-id"122" >获取id</div> 方法一:dataset()方法 //data-前缀属性可以在JS中通过dataset取值,更加方便 console.log(getId.dataset.id);//112//赋值 getId.dataset.…...
GB28181学习(五)——实时视音频点播(信令传输部分)
要求 实时视音频点播的SIP消息应通过本域或其他域的SIP服务器进行路由、转发,目标设备的实时视音频流宜通过本域的媒体服务器进行转发;采用INVITE方法实现会话连接,采用RTP/RTCP协议实现媒体传输;信令流程分为客户端主动发起和第…...
单例模式(饿汉模式 懒汉模式)与一些特殊类设计
文章目录 一、不能被拷贝的类 二、只能在堆上创建类对象 三、只能在栈上创建类对象 四、不能被继承的类 五、单例模式 5、1 什么是单例模式 5、2 什么是设计模式 5、3 单例模式的实现 5、3、1 饿汉模式 5、3、1 懒汉模式 🙋♂️ 作者:Ggggggtm &#x…...
133. 克隆图
133. 克隆图 题目-中等难度示例1. bfs 题目-中等难度 给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。 图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])。…...
交流耐压试验目的
试验目的 交流耐压试验是鉴定电力设备绝缘强度最有效和最直接的方法。 电力设备在运行中, 绝缘长期受着电场、 温度和机械振动的作用会逐渐发生劣化, 其中包括整体劣化和部分劣化,形成缺陷, 例如由于局部地方电场比较集中或者局部…...
使用 YCSB 和 PE 进行 HBase 性能压力测试
HBase主要性能压力测试有两个,一个是 HBase 自带的 PE,另一个是 YCSB,先简单说一个两者的区别。PE 是 HBase 自带的工具,开箱即用,使用起来非常简单,但是 PE 只能按单个线程统计压测结果,不能汇…...
正则表达式相关概念及不可见高度页面的获取
12.正则 概念:匹配有规律的字符串,匹配上则正确 1.正则的创建方式 构造函数创建 // 修饰符 igm// i 忽视 ignore// g global 全球 全局// m 换行 var regnew RegExp("匹配的内容","修饰符")var str "this is a Box";var reg new RegExp(&qu…...
深入学习 Redis - 分布式锁底层实现原理,以及实际应用
目录 一、Redis 分布式锁 1.1、什么是分布式锁 1.2、分布式锁的基础实现 1.2.1、引入场景 1.2.2、基础实现思想 1.2.3、引入 setnx 1.3、引入过期时间 1.4、引入校验 id 1.5、引入 lua 脚本 1.5.1、引入 lua 脚本的原因 1.5.2、lua 脚本介绍 1.6、过期时间续约问题&…...
Hive行转列[一行拆分成多行/一列拆分成多列]
场景: hive有张表armmttxn_tmp,其中有一个字段lot_number,该字段以逗号分隔开多个值,每个值又以冒号来分割料号和数量,如:A3220089:-40,A3220090:-40,A3220091:-40,A3220083:-40,A3220087:-40,A3220086:-4…...
TypeScript系列之类型 string
文章の目录 背景写在最后 背景 与JavaScript不同的是,TypeScript使用的是静态类型,比如说它指定了变量可以保存的数据类型。如下面代码所示,如果在JavaScript中,指定变量可以保存的数据类型,会报错:类型注…...
【C++】动态内存管理 ③ ( C++ 对象的动态创建和释放 | new 运算符 为类对象 分配内存 | delete 运算符 释放对象内存 )
文章目录 一、C 对象的动态创建和释放1、C 语言 对象的动态创建和释放 的方式2、C 语言 对象的动态创建和释放 的方式 二、代码示例 - 对象的动态创建和释放 一、C 对象的动态创建和释放 使用 C 语言中的 malloc 函数 可以为 类对象 分配内存 ; 使用 free 函数可以释放上述分配…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南 背景介绍完整操作步骤1. 创建Docker容器环境2. 验证GUI显示功能3. 安装ROS Noetic4. 配置环境变量5. 创建ROS节点(小球运动模拟)6. 配置RVIZ默认视图7. 创建启动脚本8. 运行可视化系统效果展示与交互技术解析ROS节点通…...
解析“道作为序位生成器”的核心原理
解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制,重点解析"道作为序位生成器"的核心原理与实现框架: 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...
