SpringSecurity中,SecurityContextHolder工具类初步解析
Spring Security
中最基本的组件应该是SecurityContextHolder
了。这是一个工具类,只提供一些静态方法。这个工具类的目的是用来保存应用程序中当前使用人的安全上下文。
一个应用可能有多个用户,SecurityContextHolder
中使用ThreadLocal
机制保存每个使用者的安全上下文,具体可看源码:
private static void initializeStrategy() {
if ("MODE_PRE_INITIALIZED".equals(strategyName)) {
Assert.state(strategy != null, "When using MODE_PRE_INITIALIZED, setContextHolderStrategy must be called with the fully constructed strategy");
} else {
if (!StringUtils.hasText(strategyName)) {
strategyName = "MODE_THREADLOCAL";
}
if (strategyName.equals("MODE_THREADLOCAL")) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
} else if (strategyName.equals("MODE_INHERITABLETHREADLOCAL")) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
} 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 var2) {
ReflectionUtils.handleReflectionException(var2);
}
}
}
}
其中strategyName
是SecurityContextHolder
的私有成员变量,SecurityContextHolder
有三种工作模式
- MODE_THREADLOCAL(默认模式)
- MODE_INHERITABLETHREADLOCAL
- MODE_GLOBAL
以MODE_THREADLOCAL为例,介绍ThreadLocalSecurityContextHolderStrategy
类,代码如下:
final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal();
ThreadLocalSecurityContextHolderStrategy() {
}
public void clearContext() {
contextHolder.remove();
}
public SecurityContext getContext() {
SecurityContext ctx = (SecurityContext)contextHolder.get();
if (ctx == null) {
ctx = this.createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
public void setContext(SecurityContext context) {
Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
contextHolder.set(context);
}
public SecurityContext createEmptyContext() {
return new SecurityContextImpl();
}
}
可以看到,初始化了ThreadLocal
类型的私有成员变量contextHolder
,在getContext()
方法中,使用contextHolder.set(ctx)
,以contextHolder
作为键,向不同主线程的ThreadLocalMap
类型的成员变量中放置了不同的Context对象,用来存储不同的用户信息。
接下来介绍一下三种工作模式的特点:
MODE_THREADLOCAL(默认模式):将SecurityContext 放在当前线程的 ThreadLocal 变量中。这意味着在同一线程内,不同的方法和代码段都可以访问相同的 SecurityContext,并因此共享用户数据。但是,当开启子线程时,子线程无法获取到用户数据,因为 ThreadLocal 不会被自动传递给子线程。
MODE_INHERITABLETHREADLOCAL:与 MODE_THREADLOCAL 类似,SecurityContext 也存储在 ThreadLocal 中,但它允许子线程访问相同的 SecurityContext。当你在主线程中创建子线程时,SecurityContext 可以被子线程继承,这意味着子线程也能够访问用户数据。
MODE_GLOBAL:数据保存到一个静态变量中,而不是线程本地变量。这种模式在 web 开发中很少使用,因为它将共享相同的 SecurityContext 实例给所有线程,可能会导致并发问题。一般情况下,它不适合多线程环境,因此在 web 应用中并不常见。
再以一段代码阐述MODE_THREADLOCAL的含义:
public class MyTask implements Runnable {
@Override
public void run() {
// 在子线程中,没有自动传递的 SecurityContext
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// authentication 为 null
}
}
public static void main(String[] args) {
// 在主线程中设置 SecurityContext
SecurityContextHolder.getContext().setAuthentication(/* ... */);
// 启动子线程
Thread thread = new Thread(new MyTask());
thread.start();
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能