Async和SpringSecurityContext
场景回溯
在执行一个用时较长的批量插入业务的时候,我尝试使用@Async异步对业务进行优化,但是却给我报了空指针的错误,定位之后发现
此处我是基于SpringSecurity来获取用户的
是currentUserService获取到的当前登陆用户为空导致的,但是当前确实是处于登陆状态的
然后,我删除了业务方法上的@Async注解,这个方法执行了20s但是没有出现报错
由此可以确定是异步导致的错误
错误原因
SecurityContextHolder的底层默认是基于ThreadLocal的,
基于ThreadLocal就会导致异步执行的子线程拿不到主线程的ThreadLocal,从而导致SecurityContext中没有用户信息;
解决方式
既然子线程没有拿到父线程的ThreadLocal那就让他拿到不就好了,那我们该如何拿到呢?
InheritableThreadLocal
是Java中的一个类,它提供了类似于ThreadLocal
的功能,但具有额外的特性。它允许在ThreadLocal
中存储的值在创建子线程时被子线程继承。
那我们如何让SecurityContext底层使用InheritableThreadLocal
呢?
SpringSecurity贴心的为我们设计了基于InheritableThreadLocal
的SecurityContext策略
并且在SecurityContextHolder
中已经给出了对应的配置项
我们只需要进行相应的配置即可
public static void main(String[] args) {
SpringApplication.run(LabourServiceApplication.class,args);
//配置基于InheritableThreadLocal的SecurityContext
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}