关于 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()
                || authentication instanceof AnonymousAuthenticationToken) {
            log.error("Not authenticated");
            return null;
    }

    [...]

}

我正在使用 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
    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。

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 策略?


根据对 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 或更高版本,则支持开箱即用。否则需要一些实现,但这将是另一个问题。