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。否则需要一些实现,但那将是另一个问题。