Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 在多线程上下文中使用OAuth2RestTemplate_Multithreading_Spring Security_Spring Boot_Spring Security Oauth2_Resttemplate - Fatal编程技术网

Multithreading 在多线程上下文中使用OAuth2RestTemplate

Multithreading 在多线程上下文中使用OAuth2RestTemplate,multithreading,spring-security,spring-boot,spring-security-oauth2,resttemplate,Multithreading,Spring Security,Spring Boot,Spring Security Oauth2,Resttemplate,我在我的Spring boot应用程序中使用OAuth2RestTemplate,并通过它使用一些资源,因为它封装了所有的身份验证信息,所以我可以只发送请求,而不用担心令牌和其他身份验证内容 在我并行发送请求之前,一切正常 由于OAuth2RestTemplate有一个会话作用域(该作用域是本地的,因为它包含用户的会话相关信息),当我试图在多线程环境中使用它时,我得到以下异常 org.springframework.beans.factory.BeanCreationException:创建名为

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

在我并行发送请求之前,一切正常

由于OAuth2RestTemplate有一个
会话
作用域(该作用域是本地的,因为它包含用户的会话相关信息),当我试图在多线程环境中使用它时,我得到以下异常

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);
有一个类似的问题被讨论过,但我仍然希望有一些与OAuth2RestTemplate相关的解决方案


所以我想知道是否有人遇到了这个问题,以及您是如何解决的。

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

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

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

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

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

    return restTemplate;
}
特别是,自动连接的
restemplate
中不存在请求工厂,事实上它是
null
。此外,我实际上有多个不同的
OAuth2RestTemplate
bean,但是
@Autowired
为所有bean返回了完全相同的实例

该键原来位于
OAuth2RestorOperations配置中:

@Bean
@Primary
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext,
        OAuth2ProtectedResourceDetails details) {
    OAuth2RestTemplate template = new OAuth2RestTemplate(details,
            oauth2ClientContext);
    return template;
}
由于
@Primary
注释,所有
@Autowired
实例显然都将获得此注释。在本例中,通过参数名指定您想要获得的bean(以前通常是有效的)没有这样做

添加
@限定符
注释解决了此问题:

@Autowired
@Qualifier("myOwnOAuth2RestTemplate")
OAuth2RestTemplate oAuth2RestTemplate;
现在,将返回正确的实例,并且在发出请求时,将调用auth服务器以获取OAuth2令牌,如
security.OAuth2.client
中的应用程序配置中所配置的那样


但是,我没有尝试从新线程中自动连接
restemplate
。相反,我将其作为一个论点传递。我不完全确定这是否会产生影响,但由于
getAccessToken()
是在请求执行期间由
RestTemplate
调用的,因此我假设它在自动连接到新线程时也会起作用

您已经创建了bean单例,因此它可以很好地工作,因为Spring可以处理将单例连接到不同的线程。但这里的问题是,oAuth2RestTemplate在默认情况下有一个会话范围来存储用户的相关身份验证信息,因此每个用户都可以使用自己的bean。我不确定这是否能正确使用单例oAuth2RestTemplate。@ikryvorotenko鉴于您希望在
SecurityContext
中使用已验证主体的OAuth2访问令牌,实际上并不需要每个用户都有一个单独的bean。您可以使用
SecurityContext
Authentication
中的令牌更新
OAuth2ClientContext
。我认为会话范围的bean并不是“真正的”单例,因为Spring的代理魔法。事实上,基于我在回答中描述的方法,我们成功地使用了不同的
OAuth2RestTemplate
bean,以及使用静态用户名/密码凭据来获取令牌的其他bean。这不起作用-至少对我来说-甚至我试图禁用spring已经实例化的
RequestContextFilter
,并使用
threadContextInheritable
as
true
创建自己的。找到此处建议的解决方案,效果非常好: