我在我的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相关的解决方案。
所以我想知道是否有人满足这一点以及你是如何解决它的。
答案 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
调用,我认为它应该在自动装入新线程时起作用。