这是我的场景:
我的应用程序启用了 Mongo 审核,并带有自定义 AuditorAware,可从SecurityContext
获取当前用户。这适用于同步方法,并且已成功保存当前审核员,但我无法使用@Async
方法使其正常工作。
我有一个异步方法(CompletableFuture
(,可以对我的Mongo数据库进行一些更新。调用AuditorAware.getCurrentAuditor()
时,不存在身份验证信息,并且我无法获取当前审核员(SecurityContextHolder.getContext().getAuthentication()
返回null
(。
@Override
public User getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()
|| authentication instanceof AnonymousAuthenticationToken) {
log.error("Not authenticated");
return null;
}
[...]
}
我正在使用DelegatingSecurityContextAsyncTaskExecutor
:
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(200);
executor.initialize();
return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new ItacaExceptionHandler();
}
}
如何使其正常工作?
Spring 安全上下文始终绑定到 Threadlocal。
您可能还需要为安全上下文设置MODE_INHERITABLETHREADLOCAL。
@Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
methodInvokingFactoryBean.setTargetMethod("setStrategyName");
methodInvokingFactoryBean.setArguments(new String[]{SecurityContextHolder.MODE_INHERITABLETHREADLOCAL});
return methodInvokingFactoryBean;
}
http://www.ogrigas.eu/spring/2010/04/inherit-spring-security-context-in-child-threads
如何设置 Spring Security SecurityContextHolder 策略?
在对 kuhajeyan 的回答的评论之后,您似乎没有正确使用 Spring @Async
中的CompletableFuture
。
如果您通过以下方式启动任务,例如 CompletableFuture.supplyAsync(Supplier)
,它们将由公共ForkJoinPool
执行,而不是您为 @Async
配置的那个。您可以使用将Executor
作为参数的重载,但它实际上不会从@Async
的优点中受益。
相反,你应该做的是让 Spring 处理任务执行,并简单地返回一个完整的CompletableFuture
,如下所示:
@Async
public CompletableFuture<String> someMethod() {
// do some computation, but return a completed future
return CompletableFuture.completedFuture("Hello World!");
}
然后,Spring 将在配置的执行器中异步执行您的方法,同时立即返回一个CompletableFuture
该该将在您的方法返回时完成。
如果您使用的是 Spring 4.2 或更高版本,则开箱即用。否则需要一些实现,但这将是另一个问题。