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 函数可以释放上述分配…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
