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();
}
- SecurityContextHolder SpringSecurity 工具securitycontextholder springsecurity工具 securitycontextholder securitycontextholder securitycontext authentication securitycontextholder getauthentication springboot springsecurity springsecurity流程 springsecurity教程 springsecurity-jwt springsecurity权限 springsecurity1