关于 Java Future:Java Future – Spring Authentication is null into AuditorAware
Java Future - Spring Authentication is null into AuditorAware
这是我的场景:
我的应用启用了 Mongo 审计,并使用自定义的 AuditorAware 从 SecurityContext 获取当前用户。这适用于同步方法,并且当前审核员已成功保存,但我无法使其与 @Async 方法正常工作。
我有一个异步方法 (CompletableFuture) 可以对我的 Mongo 数据库进行一些更新。当调用 AuditorAware.getCurrentAuditor() 时,不存在身份验证信息,并且我无法获取当前审计员(SecurityContextHolder.getContext().getAuthentication() 返回 null)。
|
1
2 3 4 5 6 7 8 9 10 11 12 13 |
@Override
public User getCurrentAuditor() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null || !authentication.isAuthenticated() [...] } |
我正在使用 DelegatingSecurityContextAsyncTaskExecutor:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@Configuration
@EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override return new DelegatingSecurityContextAsyncTaskExecutor(executor); @Override } |
我怎样才能让它正常工作?
Spring 安全上下文总是绑定到 Threadlocal。
您可能还可以为安全上下文额外设置 MODE_INHERITABLETHREADLOCAL。
|
1
2 3 4 5 6 7 8 |
@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.origas.eu/spring/2010/04/inherit-spring-security-context-in-child-threads
如何设置 Spring Security SecurityContextHolder 策略?
相关讨论
- 根据这个问题,使用 MODE_INHERITABLETHREADLOCAL 在使用重用线程的 Executor 时将不起作用(继承仅在创建线程时发生)。但是它应该与 DelegatingSecurityContextAsyncTaskExecutor 一起使用。 @ s1moner3d 你确定你在问题中显示的执行者确实被使用了吗? CompletableFuture 默认使用通用的 ForkJoinPool。
- @DidierL 我该如何检查?我确信它是在启动时实例化的
- @DidierL 我检查过,你是绝对正确的!它使用 ForkJoinPool。我怎样才能让它使用 DelegatingSecurityContextAsyncTaskExecutor?
- @s1moner3d 我想最简单的方法是调试其中一个 @Async 方法以查看它在哪个线程中运行,或者在该方法中打印 Thread.getCurrentThread().getName() 。
- @s1moner3d 这取决于您如何创建 CompletableFuture。大多数方法都有一个带有 Executor 参数的重载,但是如果你依赖 Spring @Async 我猜你有一个工厂来处理它们的创建,所以应该在那里完成。
- 我是 Future 的新手。我已经发布了 AsyncConfigurer,并且我得到了 CompletableFuture.supplyAsync() 的任务。如何自动装配我的 DelegatingSecurityContextAsyncTaskExecutor 并将其传递给 supplyAsync ?
根据对 kuhajeyan 的回答的评论,您似乎没有正确使用 CompletableFuture 和 Spring @Async。
如果您使用例如启动任务CompletableFuture.supplyAsync(Supplier),它们将由公共 ForkJoinPool 执行,而不是您为 @Async 配置的那个。您可以使用以 Executor 作为参数的重载,但它实际上不会受益于 @Async.
的优点
相反,你应该做的是让 Spring 处理任务执行,并简单地返回一个完整的 CompletableFuture,如下所示:
|
1
2 3 4 5 |
@Async
public CompletableFuture<String> someMethod() { // do some computation, but return a completed future return CompletableFuture.completedFuture("Hello World!"); } |
Spring 将在配置的执行器中异步执行您的方法,同时立即返回一个 CompletableFuture,该方法将在您的方法返回时完成。
如果您使用的是 Spring 4.2 或更高版本,则支持开箱即用。否则需要一些实现,但这将是另一个问题。