我正在开发一个带有feign and oauth2的弹簧云项目。为了获得更好的用户体验,这些操作已转移到异步方法(带有@ASYNC(中。但是出现了一个问题。我将OAuth2FeignRequestInterceptor添加为BEAN,并确保假定客户端可以在同步方法中正常工作(哪个线程在RequestContexTholder中具有正确的请求attributes(。
@Configuration
public class SomeConfiguration{
@Bean
public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, BaseOAuth2ProtectedResourceDetails resource){
return new OAuth2FeignRequestInterceptor(oAuth2ClientContext,resource);
}
}
但是,如果我将这些操作移动到异步方法中,将会出现一个例外,因为没有requestContext,因此无法创建ScopedTarget.oauth2ClientContext。我已经搜索了stackoverflow.com并找到了一个解决方案:如何在ASYNC任务执行程序中启用请求范围使用RequestContextListener bean和这些代码,RequestContexTholder属于子线程将填充Parent thread的(请求线程(requestAttributes。由于异步方法将花费一段时间在调用假装客户端之前,因此将在调用假单客户端之前对请求进行重新遵循。响应请求时,请求contextListener将通过Invoke requestContexTholder.ResetRequestAttributes((;((requestContextListListener.java:76(重置RequestContexTholder中的请求Attributes,并在request request requestAttributes中进行请求。当它完成耗时的任务并尝试通过Feign Client发送某些内容时,Feign Client尝试从请求中获取OAuth2ClientContext并引发例外:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: Cannot ask for request attribute - request is not active anymore!
我不确定OAuth2FeignRequestCtector是否适合在异步方案中从RequestContext检索授权信息。
感谢您阅读我的问题并希望您的答复。
如果您使用的是弹簧,则可以将弹簧安全上下文绑定到子线程。
SecurityContext context = SecurityContextHolder.getContext();
ExecutorService delegateExecutor = new ExecutorServiceAdapter(this.taskExecutor);
DelegatingSecurityContextExecutorService executor = new DelegatingSecurityContextExecutorService(delegateExecutor, context);
executor.invokeAll(tasks).stream()...
需要定义taskexecutor bean:
@Configuration
public class ThreadConfig {
@Bean
public TaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(10);
executor.setThreadNamePrefix("task_thread");
executor.initialize();
return executor;
}
}
最后,最重要的是,当Servlet启动时,您需要启用SetThreadContextInherabristion:
DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
dispatcherServlet.setThreadContextInheritable(true);