启用虚拟线程后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
相关产品推荐
相关产品推荐

