前言

  • • 我们通常会通过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);
    }
    ......
}