Multithreading 在多线程上下文中使用OAuth2RestTemplate
我在我的Spring boot应用程序中使用OAuth2RestTemplate,并通过它使用一些资源,因为它封装了所有的身份验证信息,所以我可以只发送请求,而不用担心令牌和其他身份验证内容 在我并行发送请求之前,一切正常 由于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:创建名为
会话
作用域(该作用域是本地的,因为它包含用户的会话相关信息),当我试图在多线程环境中使用它时,我得到以下异常
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
astrue
创建自己的。找到此处建议的解决方案,效果非常好: