在为项目配置Security的Token后,绝大部分接口运行正常,但有一两个接口却调用失败,DEBUG后发现是SecurityContextHolder.getContext().getAuthentication()
为空导致的。
该接口相关方法定义如下,可以看到使用了@Async
注解使该方法可以异步执行。
@Async@TransactionalpublicFuture<CaseEntity>run(StringexperimentId){}
查看Spring Security的文档可知
默认情况下,Spring Security Authentication 绑定到ThreadLocal. 因此,当执行流在带有 @Async 的新线程中运行时,它不会是经过身份验证的上下文。
使用DelegatingSecurityContextAsyncTaskExecutor
包装线程池可以解决这个问题,相关代码如下:
@Configuration@EnableAsyncpublicclassThreadConfig{@Bean(name="taskExecutor")publicTaskExecutorthreadPoolTaskExecutor(){ThreadPoolTaskExecutorexecutor=newThreadPoolTaskExecutor();//设置核心线程数executor.setCorePoolSize(4);//最大线程数executor.setMaxPoolSize(4);//设置队列容量executor.setQueueCapacity(20);//设置线程活跃时间executor.setKeepAliveSeconds(60);//设置线程名称前缀executor.setThreadNamePrefix("experiment-");//设置拒绝策略executor.setRejectedExecutionHandler(newThreadPoolExecutor.CallerRunsPolicy());executor.initialize();DelegatingSecurityContextAsyncTaskExecutordelegate=newDelegatingSecurityContextAsyncTaskExecutor(executor);returndelegate;}}
这个问题排查难度不大,但还是花费了一定时间,源码果真是程序员的核心竞争力,如果能阅读足够的框架源码,想必这种问题可以更快解决甚至避免。