Java 未来 - Spring AuditorAware 中的身份验证为空
Java Future - Spring Authentication is null into AuditorAware
这是我的场景:
我的应用程序启用了 Mongo 审核,并使用自定义 AuditorAware 从 SecurityContext
获取当前用户。这对同步方法很有效,并且成功保存了当前审计员,但我无法使用 @Async
方法使其正常工作。
我有一个异步方法 (CompletableFuture
) 可以对我的 Mongo 数据库进行一些更新。当调用 AuditorAware.getCurrentAuditor()
时,不存在身份验证信息,我无法获取当前审核员 (SecurityContextHolder.getContext().getAuthentication()
returns 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
How to set up Spring Security SecurityContextHolder strategy?
根据 的评论,您似乎没有正确使用 CompletableFuture
和 Spring @Async
。
如果您使用例如CompletableFuture.supplyAsync(Supplier)
, they will be executed by the common ForkJoinPool
而不是您为 @Async
配置的那个。您可以使用将 Executor
作为参数的重载,但它实际上不会受益于 @Async
.
的优势
相反,您应该做的是让 Spring 处理任务执行,然后简单地 return 一个 completed CompletableFuture
这样:
@Async
public CompletableFuture<String> someMethod() {
// do some computation, but return a completed future
return CompletableFuture.completedFuture("Hello World!");
}
Spring 然后将在配置的执行程序中异步执行您的方法,同时立即 return a CompletableFuture
将在您的方法 returns.
如果您使用的是 Spring 4.2 或更高版本,this is supported out of the box。否则需要一些实现,但那将是另一个问题。
这是我的场景:
我的应用程序启用了 Mongo 审核,并使用自定义 AuditorAware 从 SecurityContext
获取当前用户。这对同步方法很有效,并且成功保存了当前审计员,但我无法使用 @Async
方法使其正常工作。
我有一个异步方法 (CompletableFuture
) 可以对我的 Mongo 数据库进行一些更新。当调用 AuditorAware.getCurrentAuditor()
时,不存在身份验证信息,我无法获取当前审核员 (SecurityContextHolder.getContext().getAuthentication()
returns 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
How to set up Spring Security SecurityContextHolder strategy?
根据 CompletableFuture
和 Spring @Async
。
如果您使用例如CompletableFuture.supplyAsync(Supplier)
, they will be executed by the common ForkJoinPool
而不是您为 @Async
配置的那个。您可以使用将 Executor
作为参数的重载,但它实际上不会受益于 @Async
.
相反,您应该做的是让 Spring 处理任务执行,然后简单地 return 一个 completed CompletableFuture
这样:
@Async
public CompletableFuture<String> someMethod() {
// do some computation, but return a completed future
return CompletableFuture.completedFuture("Hello World!");
}
Spring 然后将在配置的执行程序中异步执行您的方法,同时立即 return a CompletableFuture
将在您的方法 returns.
如果您使用的是 Spring 4.2 或更高版本,this is supported out of the box。否则需要一些实现,但那将是另一个问题。