0

我正在开发一个使用Feign和OAuth2的spring cloud项目。项目中有一些耗时的操作,这些操作完成后会发送一些请求。为了获得更好的用户体验,这些操作被移到了异步方法中(使用@Async)。但是出现了一个问题。我将 OAuth2FeignRequestInterceptor 添加为 bean,并确保 Feign Client 可以在同步方法中正常工作(哪个线程在 RequestContextHolder 中具有正确的 RequestAttributes)。

@Configuration
public class SomeConfiguration{
@Bean
    public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, BaseOAuth2ProtectedResourceDetails resource){
        return new OAuth2FeignRequestInterceptor(oAuth2ClientContext,resource);
    }
}

但是如果我将这些操作移到异步方法中,就会抛出一个异常,即由于缺少RequestContext而无法创建scopedTarget.oauth2ClientContext。我搜索了stackoverflow.com并找到了一个解决方案: 如何在异步任务执行器中启用请求范围 使用 RequestContextListener bean 和这些代码,属于子线程的 RequestContextHolder 将填充父线程(请求线程)的 RequestAttributes。因为异步方法在调用 feign 客户端之前会花费一些时间,所以请求会在调用 feign 客户端之前得到响应。当请求得到响应时,RequestContextListener 将通过调用 RequestContextHolder.resetRequestAttributes();(RequestContextListener.java:76) 来重置 RequestContextHolder 中的 RequestAttributes,并使 RequestAttributes 中的请求处于非活动状态。当它完成耗时的任务并尝试通过 feign 客户端发送一些东西时,feign 客户端尝试从请求中获取 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!

我不确定 OAuth2FeignRequestInterceptor 在异步场景中从 RequestContext 检索授权信息是否合适。

感谢您阅读我的问题并希望得到您的回复。

4

1 回答 1

-1

如果您使用的是 spring,则可以将 spring 安全上下文绑定到子线程。

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 启动时启用 setThreadContextInheritable:

DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
dispatcherServlet.setThreadContextInheritable(true);
于 2019-07-18T22:14:26.660 回答