首先需要阐明什么是SecurityContext,这是著名框架SpringSecurity 中的组件,通过一段时间的研究,我可以很负责的说,在笔者微乎其微的智商水平下,这个框架真的很难懂。
阅读前置知识:
首先我们来看一下这个契约接口所包含的具体功能有哪些?
public interface SecurityContext extends Serializable {
/**
* Obtains the currently authenticated principal, or an authentication request token.
* @return the <code>Authentication</code> or <code>null</code> if no authentication
* information is available
*/
Authentication getAuthentication();
/**
* Changes the currently authenticated principal, or removes the authentication
* information.
* @param authentication the new <code>Authentication</code> token, or
* <code>null</code> if no further authentication information should be stored
*/
void setAuthentication(Authentication authentication);
}
很简单的一个接口,可以看到它主要的功能就是维护Authentication(官方说法:认证事件)这其中含有用户的相关信息。所以这里我们可以简单下一个定义:存储Authentication的实例就是安全上下文,也就是本文的重点——SecurityContext。
接下来简单看一下它究竟是怎么起作用的:

在身份验证完成后,AuthenticationManager便会将Authentication实例存入SecurityContext,而对于我们的业务开发,我们便可以在控制层乃至于业务层去获取这部分用户信息。
我们可以从接口的定义中观察到,SecurityContext的主要职责是存储身份验证的对象,但是SecurityContext又是被怎么管理的呢?我们的SpringSecurity提供了3种管理策略,其中有这样一个充当管理者的对象——SecurityContextHolder。
三种工作模式:
在我们开是研究这个管理策略前,先谈一下它究竟该怎么设置?
//最简单的方法:注册这样一个Bean即可
@Bean
public InitializingBean initializingBean(){
return () -> SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
MODE_THREADLOCAL模式下允许每个线程在安全上下文中存储自己的信息,前提是每个请求是独立的线程处理,那么这样的话异步处理就成了问题。
做如下测试:
@Component
public class AsyncUtil {
@Async
public void test(){
SecurityContext context = SecurityContextHolder.getContext();
String name = context.getAuthentication().getName();
System.out.println("name = " + name);
}
}
@RestController
@RequestMapping("/test")
public class IndexController {
@Autowired
private AsyncUtil asyncUtil;
@GetMapping("/hello")
public String index(){
SecurityContext context = SecurityContextHolder.getContext();
String name = context.getAuthentication().getName();
System.out.println("name = " + name);
asyncUtil.test();
return "你好,服务器" + name;
}
}
我们在AsyncUtil 中尝试去取Authentication,可以惊奇的发现:
java.lang.NullPointerException: null
at com.harlon.chapter.utils.AsyncUtil.test(AsyncUtil.java:14) ~[classes/:na]
直接报错,也就直接验证了ThreadLocal的功效,
此时我们如果改成MODE_INHERITABLETHREADLOCAL便不会报错了,这里介绍一下这种模式的工作流程。

当异步开启线程后,Spring Security会为新开起的线程复制一份SecurityContext,但是这里也是有讲究的,我们所创建的线程必须是SpringSecurity所知道的线程,在本文的最后将会介绍这种情况该怎么处理。
MODE_GLOBAL其实就是所有线程共享的思路,没什么看头了。
需要提一句的是SecurityContext是非线程安全的,所以如果设置了Global,那我们就需要去关注访问并发问题。
⚠️先说结果:
SpringSecurity提供了以下多种委托对象:
| 类 | 描述 |
|---|---|
| DelegatingSecurityContextExecutor | 实现了Executor接口,并被设计用来装饰了Executor对象,使其具有安全上下文转发并创建线程池的能力。 |
| DelegatingSecurityContextExecutorService | 实现了ExecutorService接口,并被设计用来装饰ExecutorService对象,和上面作用类似。 |
| DelegatingSecurityContextScheduledExecutorService | 实现了ScheduledExecutorService,并被设计用来装饰ScheduledExecutorService对象,和上面作用类似。 |
| DelegatingSecurityContextRunnable | 实现了Runnable接口,表示新建线程执行任务并不要求响应的任务,也可以用作传播上下文。 |
| DelegatingSecurityContextCallable | 实现了Callable接口,表示新线程执行任务且返回响应的任务,也可以传播。 |
接下来抽几个做测试案例:
@GetMapping("/callable")
public String callable() throws ExecutionException, InterruptedException {
Callable<String> task = () -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication.getName();
};
ExecutorService executorService = Executors.newCachedThreadPool();
try {
var contextCallable = new DelegatingSecurityContextCallable<>(task);
return "callable 测试 : " + executorService.submit(contextCallable).get();
}finally {
executorService.shutdown();
}
}
正规测试不存在问题,这里简单测一下不适用委托的情况:
@GetMapping("/callable")
public String callable() throws ExecutionException, InterruptedException {
Callable<String> task = () -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication.getName();
};
ExecutorService executorService = Executors.newCachedThreadPool();
try {
//var contextCallable = new DelegatingSecurityContextCallable<>(task);
return "callable 测试 : " + executorService.submit(task).get();
}finally {
executorService.shutdown();
}
}
注释掉装饰器后的结果不难得知:
{
"timestamp":"2022-12-29T12:49:43.617+00:00",
"status":500,"error":"Internal Server Error",
"path":"/test/callable"
}
当然内部也就是空指针了。其实其他几个都很类,这里就不一一尝试了。
一文解决关于VLAN所有的疑惑VLAN基本概念为什么需要VLAN?怎么在交换机上划分VLAN,VLAN的工作原理有了子网,已经隔离了广播,还需要VLAN干啥?只进行子网划分,不进行VLAN划分VLAN划分与子网划分附加VLAN信息的方法VLAN划分交换机的端口类型(Access和Trunk)一、访问链接二、汇聚链接汇聚链接VLAN间通信为什么要进行VLAN间通信?路由器实现VLAN间通信路由器和交换机的连接方式通信细节三层交换机实现VLAN间通信加速VLAN间通信三层交换机与路由器三层交换机路由器路由器和交换机配合构建LAN的实例使用VLAN设计局域网的特点VLAN增加网络的灵活性不使用VLA
✅作者简介:大家好,我是小杨📃个人主页:「小杨」的csdn博客🔥系列专栏:小杨带你玩转C语言【初阶】🐳希望大家多多支持🥰一起进步呀!大家好呀!我是小杨。小杨花几天的时间将C语言中的操作符这部分知识做了一个大总结,在方便自己复习的同时也能够帮助到大家。通篇字数在一万字左右,可以算作是非常详细了,一文就可以带领大家彻底掌握操作符这部分内容,文章很长建议先收藏再看,防止下次想看就找不到啦。文章目录✍1,算术操作符✍2,移位操作符 🔍2.1,左移操作符 🔍2.2,右移操作符 ✨2.2.1,算术移位 ✨2.2.2,逻辑移位✍3,位操作符 🔍3.1,按位与&
每个企业都希望在完成项目后获得盈利,但不少企业到了年终后才发现项目做了不少,公司却并没能达到预期,甚至还出现了亏损。那么钱究竟去了哪里?很多公司都搞不清楚原因,出现糊涂账较多的状况,这将会造成严重的后果,尤其在疫情影响下,大环境很恶劣,如果是大公司的事业部门出现亏损,就可能会导致事业部门解散;如果是小公司出现亏损,就很容易导致公司倒闭;怎样做才能确保我们所完成的项目都能获利?从财务角度看,要确保盈利必须做到合理估算成本,只有这样才能在对外签订合约时做出合理报价,在对内在开始项目前做出充分评估投入代价,同时在实施过程中还要控制成本得当,最后项目结束时才会有可能获得盈利。那么我们怎样才能准确的判断
COINDAO旨在重建社区信任和安全。基于皖北基因的强烈共识,COINDAO自发产生了一个共创、共建、共治、共享的协作组织。它专注于DAO投资管理协议,为新的优质项目创造增长技术和资金。COINDAO的使命就是为真正的优质项目打造一个去中心化、公开透明的平台,让各个赛道上的优质项目能够以更低的成本快速募集资金并向公众开放.打破头部垄断。让真正的爱好者直接获得早期参与优质项目的资格,不再遥不可及,构建生态应用的可信体系,打造人人参与共建、人人共享的去中心化DAO好处。生态系统,我们称之为COINDAO生态系统。COINDAO国内各大财经网站宣发如下:COINDAO国外各大财经网站宣发: COIN
今天来说说前端低代码有多幸福?低代码是啥?顾名思义少写代码……这种情况下带来的幸福有:代码写得少,bug也就越少(所谓“少做少错”),因此开发环节的两大支柱性工作“赶需求”和“修bug”就都少了;要测的代码少了,那么测试用例也可以少写了。所以,总结低代码带来的幸福感有这三大点:开发效率提高开发成本减少维护性更高针对上述三点,我们展开说说。01、开发效率提高对于低代码的理解,个人认为可以通过配置化的低成本交互方式(主流是拖拽)加上少量的胶水代码,去满足一类应用的需求。这就说明,基于低代码,开发人员无需代码或说只需少量代码就可以开发出各类应用管理系统,如:OA协同办公、KM知识管理、CRM客户关系
目录🍊前言🍊:🍈一、宏与函数🍈: 1.宏与函数对比: 2.宏与函数的命名约定:🍓二、预处理操作符🍓: 1.预处理操作符"#": 2.预处理操作符"##":🥝三、条件编译🥝: 1.简述条件编译指令: 2.常见条件编译指令:🍒总结🍒:🛰️博客主页:✈️銮同学的干货分享基地🛰️欢迎关注:👍点赞🙌收藏✍️留言🛰️系列专栏:💐【进阶】C语言学习 🧧 C语言学习🛰️代码仓库:🎉VS2022_C语言仓库 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路过的友友麻烦多多点赞关注,欢迎你们的私信提问,感谢你们的转发!
作者:翟天保Steven版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处一、设计模式是什么? 设计模式是为了解决在软件开发过程中遇到的某些问题而形成的思想。同一场景有多种设计模式可以应用,不同的模式有各自的优缺点,开发者可以基于自身需求选择合适的设计模式,去解决相应的工程难题。 良好的软件设计和架构,可以让代码具备良好的可读性、可维护性、可扩展性、可复用性,让整个系统具备较强的鲁棒性和性能,减少屎山代码出现的概率。 想要熟练运用设计模式,提高自己的编程能力和架构能力,只有在自己工作中,结合自身工作内容,多思考多实践。本文只能通过举一些通俗的例子,来
自今年4月以来,在经历了UST脱锚,所引发的头部投资机构、独角兽CeFi平台的相继“破产”,加密行业迎来了前所未有的发展危机,一方面在于市场信心不足甚至恐慌,导致的资金加速出逃,另一方面市场整体增速缓慢导致各个板块收益疲软,导致投资者们难以获得可观的收益回报,市场进一步陷入新一轮的恶性循环。当然,即便是市场整体处于下行周期,但加密市场仍旧存在诸多的获利机会,新型LaaS协议ElephantSwap正在通过其独特的LaaS方案,来为DeFi市场恢复流动性注入信心,同时为投资者带来十分可观的套利空间。目前,PlatoFarm是首个使用ElephantSwap流动性服务的项目,投资可以通过将手中的P
系列文章目录第一章Android:彻底搞懂Lifecycle——使用篇第二章Android:彻底搞懂Lifcycle——原理篇文章目录系列文章目录前言一、Lifecycle是什么?1.应用场景2.示例二、Lifecycle使用1.泳(用)裤(库)第一步是什么?——先引入2.Lifecycle类3.LifecycleOwner接口3.1.自定义类实现LifecycleOwner接口总结前言你真的了解lifecycle吗?本文的目标就是要“打破砂锅问到底”,带你真真切切了解它。一、Lifecycle是什么?lifecycle是属于AndroidJetpack(官方开发工具包)——Architect
一、简介1.1Flyway是什么?Flyway是一款开源的数据库版本管理工具,可以实现管理并跟踪数据库变更,支持数据库版本自动升级,而且不需要复杂的配置,能够帮助团队更加方便、合理的管理数据库变更。例:创建两个sql变更文件,项目启动后会将两个文件中的sql语句全部执行。1.2为什么使用Flyway?简单举个例子:开发时,如果A开发和B开发都对同一数据库进行了修改,那么如何进行数据同步呢?假如多个开发人员都修改了sql脚本,怎么同步到测试环境和生产环境?类似于以上的情况在日常开发中不胜枚举,在最开始的单体架构中,我们公司采用了通过校验数据库版本号来实现sql的变更,这虽然能够解决大部分问题,但