[SpringSecurity5.6.2源码分析九]:SecurityContextHolder
原创
©著作权归作者所有:来自51CTO博客作者wx5bb9ee7a9253c的原创作品,请联系作者获取转载授权,否则将追究法律责任
前言
- • 我们通常会通过SecurityContextHolder.getContext()获取安全上下文
- • 然后通过SecurityContext.getAuthentication()获取认证对象
- • 最后通过Authentication.getPrincipal()获取用户对象,也就是下面的步骤
- • SecurityContextHolder.getContext().getAuthentication().getPrincipal();
1、SecurityContextHolder
- • SecurityContextHolder作为获取SecurityContext的帮助类,其安全上下文的读取策略有三种
- • GlobalSecurityContextHolderStrategy:全局的
- • ThreadLocalSecurityContextHolderStrategy:借助ThreadLocal保存
- • InheritableThreadLocalSecurityContextHolderStrategy:借助InheritableThreadLocal保存
- • 可以看到这里借助了静态代码块,静态代码块是在当前类被加载-连接-初始化的阶段执行
*/
static {
initialize();
}
private static void initialize() {
initializeStrategy();
initializeCount++;
}
- • initializeStrategy(): 根据strategyName的值初始化安全上下文的存储策略,
- • 默认没有配置使用ThreadLocalSecurityContextHolderStrategy
- • 除了SpringSecurity默认提供的三种,还支持实现SecurityContextHolderStrategy来自定义
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");
return;
}
//如果没有设置过使用的策略,就默认使用ThreadLocal作为存储策略
if (!StringUtils.hasText(strategyName)) {
// Set default
strategyName = MODE_THREADLOCAL;
}
//使用ThreadLocal作为线程级别安全上下文存储策略
if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
return;
}
//使用InheritableThreadLocal作为线程级别安全上下文存储策略
if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
return;
}
//使用Global作为线程级别安全上下文存储策略
if (strategyName.equals(MODE_GLOBAL)) {
strategy = new GlobalSecurityContextHolderStrategy();
return;
}
//尝试加载自定义的线程级别安全上下文存储策略
try {
Class<?> clazz = Class.forName(strategyName);
Constructor<?> customStrategy = clazz.getConstructor();
strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
- • strategyName默认是从系统变量中加载的
private static String strategyName = System.getProperty(SYSTEM_PROPERTY);
- • 当然也支持后续执行set方法重新加载,这有可能影响到以前保存的
public static void setStrategyName(String strategyName) {
SecurityContextHolder.strategyName = strategyName;
initialize();
}
2、ListeningSecurityContextHolderStrategy
- • SecurityContextHolderStrategy在5.2.2中有三个默认实现
- • GlobalSecurityContextHolderStrategy:全局的
- • ThreadLocalSecurityContextHolderStrategy:借助ThreadLocal保存
- • InheritableThreadLocalSecurityContextHolderStrategy:借助InheritableThreadLocal保存
- • 但我在5.6中发现多了一个实现:ListeningSecurityContextHolderStrategy
- • 此类是一个委托机制的实现,内部还是维护了其策略
private final SecurityContextHolderStrategy delegate;
public final class ListeningSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
......
private final Collection<SecurityContextChangedListener> listeners;
private final SecurityContextHolderStrategy delegate;
@Override
public void clearContext() {
SecurityContext from = getContext();
this.delegate.clearContext();
publish(from, null);
}
@Override
public void setContext(SecurityContext context) {
SecurityContext from = getContext();
this.delegate.setContext(context);
publish(from, context);
}
......
}