概述
SecurityContextHolder
可以拆分为两个单词 SecurityContext
和 Holder
,Holder
意思是 持有
的意思,所以 SecurityContextHolder
的大概意思就是这个类中存放了 SecurityContext
。也就是说我们首先要知道 SecurityContext
是什么
SecurityContext
/**
* Interface defining the minimum security information associated with the current thread
* of execution.
*
* <p>
* The security context is stored in a {@link SecurityContextHolder}.
* </p>
*/
public interface SecurityContext extends Serializable {
Authentication getAuthentication();
void setAuthentication(Authentication authentication);
}
这个接口只有一个实现类
SecurityContextImpl
,里面没什么内容,这里就不粘源码了。
从源码注释中我们知道,SecurityContext
是一个接口,这个接口定义了与当前执行的线程所关联的最小安全信息,接口定义了两个方法,set
和 get
,针对 Authentication
:
public interface Authentication extends Principal, Serializable {
// 授权信息
Collection<? extends GrantedAuthority> getAuthorities();
Object getCredentials();
Object getDetails();
// 认证主体
Object getPrincipal();
boolean isAuthenticated();
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
Authentication
继承自Principal
,而Principal
是Java
提供的
Authentication
是请求认证成功后的令牌,里面包含了认证主体 Principal
和授权信息 Authorities
。知道了这两个概念以后,接下来我们看一下 SecurityContextHolder
SecurityContextHolder
public class SecurityContextHolder {
// 预设 3 种模式
public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
public static final String MODE_GLOBAL = "MODE_GLOBAL";
// 可以系统变量中指定使用哪种模式
public static final String SYSTEM_PROPERTY = "spring.security.strategy";
// 可以系统变量中指定使用哪种模式
private static String strategyName = System.getProperty(SYSTEM_PROPERTY);
// SecurityContext 的持有策略接口,也就是上面 3 中模式的功能接口定义
private static SecurityContextHolderStrategy strategy;
// 初始化次数
private static int initializeCount = 0;
// 1、第一步,会执行 initialize 方法
static {
initialize();
}
private static void initialize() {
if (!StringUtils.hasText(strategyName)) {
// Set default
// 默认会设置为这种策略
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 to load a custom strategy
try {
Class<?> clazz = Class.forName(strategyName);
Constructor<?> customStrategy = clazz.getConstructor();
strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
initializeCount++;
}
// 下面几个方式是定义的操作 SecurityContext 的方法,主要是对 SecurityContext 的增删改查
public static void clearContext() {
strategy.clearContext();
}
public static SecurityContext getContext() {
return strategy.getContext();
}
public static int getInitializeCount() {
return initializeCount;
}
public static void setContext(SecurityContext context) {
strategy.setContext(context);
}
public static void setStrategyName(String strategyName) {
SecurityContextHolder.strategyName = strategyName;
initialize();
}
public static SecurityContextHolderStrategy getContextHolderStrategy() {
return strategy;
}
public static SecurityContext createEmptyContext() {
return strategy.createEmptyContext();
}
@Override
public String toString() {
return "SecurityContextHolder[strategy='" + strategyName + "'; initializeCount=" + initializeCount + "]";
}
}
从 SecurityContextHolder
的源码中我们知道默认是使用 ThreadLocalSecurityContextHolderStrategy
这种模式,也就是说底层是使用 ThreadLocal
来存储 SecurityContext
的。
final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
@Override
public void clearContext() {
contextHolder.remove();
}
@Override
public SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
@Override
public void setContext(SecurityContext context) {
Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
contextHolder.set(context);
}
@Override
public SecurityContext createEmptyContext() {
return new SecurityContextImpl();
}
}
源码比较简单,没什么内容,就不做过多介绍了。
总结
以上就是关于 SecurityContextHolder
的相关源码阅读,通过分析我们知道:
SecurityContextHolder
为每一个执行中的线程关联了SecurityContext
- 在认证通过之后,应该通过
SecurityContextHolder
来设置SecurityContext
- 我们可以指定
SecurityContext
的存储策略,也可以自定义存储策略