CompletableFuture 的安全上下文为空

问题描述 投票:0回答:2

我有一个奇怪的问题。
鉴于此代码:

@RequestMapping(value = {"/randomizer"}, method = RequestMethod.POST)
    public CompletableFuture<String> randomizer(){
        CompletableFuture<String> someString = stringService
                .findRandomByInput("123")
                .thenCombine(stringService.findAnotherRandomByInput("321"), (result1, result2) -> {
                    return applyRandom(result1, result2);
                });

        CompletableFuture<Void> computation = computingService.computeRandomByInput(RandomDto.empty(), "123");

        return someString.thenCombineAsync(computation, (result1, result2) -> {
            combineIt(result1, result2, getCurrentApplicationUser());
        }, taskExecutor);
    }

通过调用

getCurrentApplicationUser()
我正在访问 spring 的
SecurityContextHolder.getContext().getAuthentication()
接口。

我有这个任务执行者:

@Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(6);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("DmpkApplication-");
        executor.initialize();
        // the following is necessary because of:
        // https://stackoverflow.com/a/57434013/7320372
        return new DelegatingSecurityContextAsyncTaskExecutor(executor);
    }

所以问题是:

我调用上述

randomizer
控制器 5 次,到第 6 次时,
getAuthentication()
调用为
null

有时对控制器的第一次调用会产生

null
,并且所有其他后续调用都会起作用。

我不知道这里出了什么问题。

spring-mvc spring-security completable-future
2个回答
1
投票

我自己找到了解决办法。

我重命名了上面的bean

public Executor taskExecutor()

以下:

public Executor executor()

现在问题已经解决了。我认为 Spring 本身已经定义了一个

executor
。之前的
taskExecutor
是另外一个。但不知怎的,Spring 的
executor
被调用了,默认情况下它不是
DelegatingSecurityContextAsyncTaskExecutor

所以也许这就是问题所在。

没关系 - 现在可以了!


0
投票

我在 Spring Boot 中遵循了以下步骤,我们还需要小心,因为代码正在关闭执行程序服务,并且一旦上下文关闭,就不会创建任何内存泄漏。

第 1 步 - 创建一个配置类,如下所示。

@Configuration
public class EnableSecurityCtxForThreadPool {

    @Bean
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(8);
        executor.setMaxPoolSize(15);
        executor.setQueueCapacity(50);
        executor.setThreadNamePrefix("async-");
        //executor.initialize(); 
// this is not need as stated above because ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
which again extends InitializingBean which call this method automatically using @Overidded afterPropertiesSet method
        return executor;
    }

    @Bean
    public DelegatingSecurityContextAsyncTaskExecutor taskExecutor
            (ThreadPoolTaskExecutor delegate) {
        return new DelegatingSecurityContextAsyncTaskExecutor(delegate);
    }
}

第2步:创建一个实用程序类,以便我们可以在代码中获取

DelegatingSecurityContextAsyncTaskExecutor
的bean,而不是自动装配ApplicationContext。

@Configuration
public class SpringUtils implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static <T> T getBean(String beanName, Class<T> clazz) {
        return context.getBean(beanName, clazz);
    }
}

第 3 步 - 创建一个像这样的 completableFuture,以便池中的所有线程都已经设置了安全上下文。

CompletableFuture<String> stringCF = CompletableFuture.supplyAsync(() -> "Hello JayZ", SpringUtils.getBean("taskExecutor", Executor.class));
© www.soinside.com 2019 - 2024. All rights reserved.