项目中使用线程池调用多线程任务通过springsecurity的api获取当前登录线程用户为空或者错误

1.问题:

项目线程池调用多线程任务,通过springsecurity的api获取不到用户或者获取错误;

SecurityContextHolder.getContext().getAuthentication()

2.查找问题:

SecurityContextHolder.getContext().getAuthentication()这个东西到底获取的是什么?

2.1先看看SecurityContextHolder.getContext()这个什么东西?

2.2 context也就是获取SecurityContextHolderStrategy里的SecurityContext对象,那这个strategy怎么来的,在哪初始化的,再看看?

 2.3 静态方法,在类初始化的执行,默认可以看到时MODE_THREADLOCAL策略,查看下项目里我们自己有没设置这个策略。

2.4 找到在集成springsecurity的配置类的时候就有设置策略模式为MODE_INHERITABLETHREADLOCAL。那就看看实例化的InheritableThreadLocalSecurityContextHolderStrategy是啥?

2.5 原来如此,我们一开始调用的SecurityContextHolder.getContext()方法,不就是调对应策略里的getContext()方法么!不同对象里contextHolder就是不同的ThreadLocal,我们获取的SecurityContext对象就是从ThreadLocal里拿的。

2.6 基于InheritableThreadLocal的特点,加上我们用线程池调用任务,你应该就差不多懂了吧。算了,还是稍微解释一下。

3.原因:

      InheritableThreadLocal这个线程变量的特点是基于每次在开启新线程时,会把主线程里的InheritableThreadLocal对象复制到子线程中。我们在调用SecurityContextHolder.getContext()获取对象的时候,如果父线程已经登录,有这个对象,我开启多线程任务,第一次创建线程,是会从父线程拿到登录对象放到子线程。但是由于我用的线程池,其他地方可能已经创建过这个线程,我只是从线程池复用这个线程做多线程任务,那么我子线程调用SecurityContextHolder.getContext()拿到的对象要么为空,要么就是这个线程之前登录过的用户信息,而不是现在父线程登录用户信息。

4.解决办法:

       在自己加一个策略类,不过换个实例化的ThreadLocal对象,用阿里提供的TransmittableThreadLocal对象,这个对象的特点就是每次调用多线程任务的时候会把父线程的TransmittableThreadLocal对象复制到子线程,所以对runnable方法包了一层,所以注意需要使用TtlRunnable对原来runnable套一层,并且改变SecurityConfig里原来配置的策略为你新写的这个,大功告成!

TransmittableThreadLocal具体原理我这里就不说了。

这样就能保证子线程每次都能从获取主线程的用户信息。

public class TransmittableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {

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

    // ~ Methods
    // ========================================================================================================

    @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();
    }
}
 for (int i = 0; i < 10; i++) {
            threadPoolTaskExecutor.execute(TtlRunnable.get(()-> System.out.println(SecurityContextHolder.getContext().getAuthentication())));
        }

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值