最近使用ruoyi框架的时候发现异步线程中无法获取用户信息的问题,现已解决<br>

原因:

Spring Security中的上下文SecurityContext的管理策略SecurityContextHolderStrategy有三种:

public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
    public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
    public static final String MODE_GLOBAL = "MODE_GLOBAL";

默认的管理策略是:MODE_THREADLOCAL,对应ThreadLocalSecurityContextHolderStrategy

# 初始化方法
 private static void initialize() {
        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);
            }
        }

        ++initializeCount;
    }

ThreadLocalSecurityContextHolderStrategy里保存context的方式是ThreadLocal,使用threadlocal子线程并不能获取父类线程的ThreadLocalMap,所以就导致了问题

private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal();

解决方法:

更改context的管理策略SecurityContextHolderStrategy为MODE_INHERITABLETHREADLOCAL,即

InheritableThreadLocalSecurityContextHolderStrategy,里面保存context的方式是InheritableThreadLocal,这时子线程就可以获取父类线程threadlocalMap了

private static final ThreadLocal<SecurityContext> contextHolder = new InheritableThreadLocal();

代码:

  • 1、可以在配置Security的配置类中添加Bean
@Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean() {
        MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
        methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
        methodInvokingFactoryBean.setTargetMethod("setStrategyName");
        methodInvokingFactoryBean.setArguments((Object) new String[]{SecurityContextHolder.MODE_INHERITABLETHREADLOCAL});
        return methodInvokingFactoryBean;
    }
  • 2、可以在配置Security的配置类中添加类初始化方式 ,会在注入SecurityContextHolder之后调用其中静态方法更改SecurityContextHolderStrategy的实现类
@PostConstruct
 public void setStrategyName(){
     SecurityContextHolder.setStrategyName(
     SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
 }
  • 3、可以在启动项加上-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL

 Spring Security异步线程无法获取用户认证信息_ThreadLocal