You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

启用虚拟线程后Spring Security异步方法认证异常求助

异步方法调用SpringSecurityContext.getToken()的认证问题

问题现象

启用虚拟线程后,异步方法中调用Token token = SpringSecurityContext.getToken();抛出AccessDeniedException,错误提示为Access forbidden: not authenticated。

错误日志如下:

2024-05-08T16:27:17.92+0530 [APP/PROC/WEB/1] OUT {"msg":"Unexpected exception occurred invoking async method: public void com.sap.dvh.messaging.EnterpriseMessagingHelper.createVehicleEvent(com.sap.dvh.dto.CreateDVHVehicleRequestDTO,com.sap.dvh.pojo.VehicleInfo) throws com.sap.dvh.exceptions.ServiceInstanceNotFoundException,com.fasterxml.jackson.core.JsonProcessingException,com.sap.dvh.exceptions.JMSProcessingException","level":"ERROR","written_ts":"1715165837925103120","logger":"org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler","exception_type":"org.springframework.security.access.AccessDeniedException","written_at":"2024-05-08T10:57:17.924Z","thread":"","type":"log","exception_message":"Access forbidden: not authenticated","stacktrace":["org.springframework.security.access.AccessDeniedException: Access forbidden: not authenticated","\tat com.sap.cloud.security.xsuaa.token.SpringSecurityContext.getToken(SpringSecurityContext.java:36)","\tat com.sap.dvh.messaging.EnterpriseMessagingConfiguration.initializeEMSConfiguration(EnterpriseMessagingConfigur...:27)","\tat com.sap.dvh.messaging.EnterpriseMessagingHelper.createVehicleEvent(EnterpriseMessagingHelper.java:74)","\tat java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unkno

触发原因

添加以下启用虚拟线程的配置后出现该问题:

@Bean
public AsyncTaskExecutor applicationTaskExecutor() {
return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
}
@Bean
public TomcatProtocolHandlerCustomizer protocolHandlerVirtualThreadExecutorCustomizer() {
return protocolHandler -> protocolHandler.setExecutor(
Executors.newVirtualThreadPerTaskExecutor());
}

已尝试的无效配置

调整TaskExecutor的Bean名称后问题仍未解决:

@Bean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
public AsyncTaskExecutor applicationTaskExecutor() {
  return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
}
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
  return protocolHandler -> protocolHandler.setExecutor(
      Executors.newVirtualThreadPerTaskExecutor());
}

解决方案

1. 配置SecurityContext传播装饰器

虚拟线程下默认的线程上下文传递机制失效,需要给异步任务添加上下文复制装饰器,手动将当前线程的SecurityContext传递到虚拟线程:

@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
    public AsyncTaskExecutor applicationTaskExecutor() {
        TaskExecutorAdapter adapter = new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
        adapter.setTaskDecorator(runnable -> {
            // 复制当前线程的SecurityContext
            SecurityContext context = SecurityContextHolder.getContext();
            return () -> {
                try {
                    SecurityContextHolder.setContext(context);
                    runnable.run();
                } finally {
                    SecurityContextHolder.clearContext();
                }
            };
        });
        return adapter;
    }
}

2. 直接传递Token到异步方法

跳过上下文传递逻辑,在调用异步方法前获取Token并作为参数传入,避免在异步线程中读取上下文:

// 调用方代码
Token currentToken = SpringSecurityContext.getToken();
enterpriseMessagingHelper.createVehicleEvent(requestDTO, vehicleInfo, currentToken);

// 异步方法修改
@Async
public void createVehicleEvent(CreateDVHVehicleRequestDTO request, VehicleInfo info, Token token) throws Exception {
    // 使用传入的token执行认证相关操作
}

3. 升级Spring Security版本

确保使用Spring Security 6.1及以上版本,该版本对虚拟线程的上下文传递做了专门适配,能减少此类兼容性问题。


内容的提问来源于stack exchange,提问作者amrit singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 04:30:20