[SpringSecurity5.6.2源码分析九]:SecurityContextHolder

前言

  • 我们通常会通过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);
    }
    ......
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值