【问题标题】:The OAuth2 authenticated feign client doesn't support be invoked in an asynchronous method?OAuth2认证的feign客户端不支持异步调用?
【发布时间】:2019-11-12 18:43:55
【问题描述】:

我正在开发一个使用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 并找到了解决方案: How to enable request scope in async task executor 使用 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 中检索授权信息是否合适。

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

【问题讨论】:

    标签: spring-cloud-feign spring-cloud-security


    【解决方案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-02-18
      • 2020-12-14
      • 1970-01-01
      • 2019-05-17
      • 1970-01-01
      • 2020-04-12
      • 2018-02-12
      • 2021-01-21
      • 1970-01-01
      相关资源
      最近更新 更多