SecurityContextHolder.getContext().getAuthentication() 为null

1,879 阅读1分钟

在为项目配置Security的Token后,绝大部分接口运行正常,但有一两个接口却调用失败,DEBUG后发现是SecurityContextHolder.getContext().getAuthentication() 为空导致的。

该接口相关方法定义如下,可以看到使用了@Async注解使该方法可以异步执行。

@Async
@Transactional
public Future<CaseEntity> run(String experimentId) {
}

查看Spring Security的文档可知

默认情况下,Spring Security Authentication 绑定到ThreadLocal. 因此,当执行流在带有 @Async 的新线程中运行时,它不会是经过身份验证的上下文。

使用DelegatingSecurityContextAsyncTaskExecutor包装线程池可以解决这个问题,相关代码如下:

@Configuration
@EnableAsync
public class ThreadConfig {
    @Bean(name = "taskExecutor")
    public TaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//        设置核心线程数
        executor.setCorePoolSize(4);
//        最大线程数
        executor.setMaxPoolSize(4);
//        设置队列容量
        executor.setQueueCapacity(20);
//        设置线程活跃时间
        executor.setKeepAliveSeconds(60);
//        设置线程名称前缀
        executor.setThreadNamePrefix("experiment-");
//        设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        DelegatingSecurityContextAsyncTaskExecutor delegate = new DelegatingSecurityContextAsyncTaskExecutor(executor);
        return delegate;
    }
}

这个问题排查难度不大,但还是花费了一定时间,源码果真是程序员的核心竞争力,如果能阅读足够的框架源码,想必这种问题可以更快解决甚至避免。