在多线程上下文

时间:2016-03-20 16:01:14

标签: multithreading spring-security spring-boot spring-security-oauth2 resttemplate

我在我的Spring启动应用程序中使用OAuth2RestTemplate并通过它处理一些资源,因为它封装了所有身份验证信息,所以我只需发送请求,而不用担心令牌和其他身份验证的东西。

一切正常,直到我并行发送请求。

由于OAuth2RestTemplate具有Session范围(本地,因为它包含用户的会话相关信息),当我尝试在多线程环境中使用它时,我得到以下异常

  

org.springframework.beans.factory.BeanCreationException:创建名称为' scopedTarget.oauth2ClientContext':范围的bean时出错   '会议'当前线程不活动;       考虑为这个bean定义一个范围代理,如果你想从一个单例引用它;       嵌套异常是java.lang.IllegalStateException:找不到线程绑定请求:您是指引用请求属性   在实际的Web请求之外,       或处理原始接收线程之外的请求?如果您实际上是在Web请求中操作而仍然   收到此消息,       您的代码可能在DispatcherServlet / DispatcherPortlet之外运行:在这种情况下,请使用   RequestContextListener或RequestContextFilter公开当前   请求。

据我所知,发生这种情况是因为正在执行代码的这些单独的线程不与会话连接。

我现在找到的唯一解决方案是在代码中手动绑定会话和新线程,但我不喜欢它。

    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

    Stream.of(1, 2, 3).parallel().forEach(it -> {
        RequestContextHolder.setRequestAttributes(requestAttributes);
        //do something
        RequestContextHolder.resetRequestAttributes();
    });

    RequestContextHolder.setRequestAttributes(requestAttributes);

有一个ticket on Spring Jira,其中讨论了类似的问题,但我仍然希望有一些与OAuth2RestTemplate相关的解决方案。

所以我想知道是否有人满足这一点以及你是如何解决它的。

2 个答案:

答案 0 :(得分:0)

对于使用会话或请求范围,您需要在@Configuration类中将RequestContextListener公开为bean。例如,请参阅this thread

答案 1 :(得分:0)

在创建自己的bean之后,我能够轻松地在多个线程中使用OAuth2RestTemplate,并确保它已正确自动装配。

在我收到完全相同的错误消息之前,事实证明我没有得到之前创建的OAuth2RestTemplate

@Bean
@Autowired
public OAuth2RestTemplate oAuth2RestTemplate(
    OAuth2ProtectedResourceDetails resourceDetails,
    SpringClientFactory clientFactory) {

    OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);
    restTemplate.setRequestFactory(new RibbonClientHttpRequestFactory(clientFactory));

    return restTemplate;
}

特别是,请求工厂没有出现在自动装配的RestTemplate中,实际上它是null。此外,我实际上有多个不同的OAuth2RestTemplate bean,但是@Autowired为所有这些bean返回了完全相同的实例。

该密钥最终位于OAuth2RestOperationsConfiguration

@Bean
@Primary
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext,
        OAuth2ProtectedResourceDetails details) {
    OAuth2RestTemplate template = new OAuth2RestTemplate(details,
            oauth2ClientContext);
    return template;
}

由于@Primary注释,所有@Autowired个实例显然都会获得此实例。根据参数名称指定要获取的bean,以前通常使用的是什么,在这种情况下没有。

添加@Qualifier注释解决了此问题:

@Autowired
@Qualifier("myOwnOAuth2RestTemplate")
OAuth2RestTemplate oAuth2RestTemplate;

现在,返回正确的实例,在发出请求时,将调用auth服务器以获取OAuth2令牌,如security.oauth2.client中的应用程序配置中所配置。

但是,我没有尝试从新线程中自动装配RestTemplate。相反,我把它作为一个参数传递。我不完全确定这是否有所不同,但由于getAccessToken()在请求执行期间被RestTemplate调用,我认为它应该在自动装入新线程时起作用。