代表多个用户使用 OAuth2RestTemplate
Using OAuth2RestTemplate on behalf of a number of users
我正在创建一个系统,该系统使用 OAuth2 身份验证的 HTTP 请求定期代表许多用户将数据导出到外部系统。
我已经能够使用 Spring Security OAuth2 与外部服务成功通信,OAuth2RestTemplate 配置如下:
@Configuration
@EnableOAuth2Client
public class ExternalServiceConfiguration {
@Autowired
private OAuth2ClientContext oauth2Context;
@Bean
public OAuth2ProtectedResourceDetails credentials() {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
details.setAccessTokenUri("https://external-service.example.com/OAuth/Token");
details.setClientId("abcdefghijklmnopq");
details.setClientSecret("123456789123456789123456789");
details.setGrantType("client_credentials");
return details;
}
@Bean
public OAuth2RestTemplate externalServiceRestTemplate() {
return new OAuth2RestTemplate(credentials(), oauth2Context);
}
}
效果很好,我可以将 OAuth2RestTemplate bean 注入到我的服务中:
@Autowired
@Qualifier("externalServiceRestTemplate")
private OAuth2RestTemplate restTemplate;
但是,在我的应用程序中,我有许多用户,每个用户都需要配置自己的客户端密钥。如果相关,我正在执行此批处理作业,这意味着它是在常规 HTTP 请求之外完成的,有时是在同一线程上下文中完成的。
这意味着我将需要多个 OAuth2ProtectedResourceDetails,并且我假设还需要多个 OAuth2RestTemplate 实例。由于这是每个用户将自行配置的内容,因此它必须根据保存在数据库中的客户端凭据动态发生。
有人对如何以高效但线程安全的方式配置动态数量的 OAuth2RestTemplate 实例有任何建议吗?
由于还没有人回复,我将尝试回答我自己的问题。
我创建了一个存储库 bean,它缓存 returns 一个基于秘密客户端密钥的 RestTemplate:
@Repository
public class ExternalServiceRepository {
private static ConcurrentHashMap<String, OAuth2RestTemplate> restTemplates = new ConcurrentHashMap<>();
/**
* Get a RestTemplate for a specific client based on it's client secret id.
* Create one if it hasn't been initialized yet.
*/
public OAuth2RestTemplate restTemplate(String clientKey) {
synchronized (restTemplates) {
OAuth2RestTemplate restTemplate = restTemplates.get(clientKey);
if (restTemplate == null) {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
details.setAccessTokenUri("https://external-service.example.com/OAuth/Token");
details.setClientId("abcdefghijklmnopq");
details.setClientSecret(clientKey);
details.setGrantType("client_credentials");
restTemplate = new OAuth2RestTemplate(details, new DefaultOAuth2ClientContext());
restTemplates.put(clientKey, restTemplate);
}
return restTemplate;
}
}
}
我没有使用为每个 HTTP 会话设置 OAuth2 客户端上下文的 @EnableOAuth2Client 注释,而是创建了我自己的 DefaultOAuth2ClientContext。由于我仅使用客户端凭据,因此我相信此代码是线程安全的(如果您不这么认为,请证明我错了)。
最后,我没有注入 RestTemplate,而是注入并使用我的存储库来访问给定客户端密钥的 restTemplate:
RestTemplate restTemplate =
externalServiceRepository.restTemplate("123456789123456789123456789");
我正在创建一个系统,该系统使用 OAuth2 身份验证的 HTTP 请求定期代表许多用户将数据导出到外部系统。
我已经能够使用 Spring Security OAuth2 与外部服务成功通信,OAuth2RestTemplate 配置如下:
@Configuration
@EnableOAuth2Client
public class ExternalServiceConfiguration {
@Autowired
private OAuth2ClientContext oauth2Context;
@Bean
public OAuth2ProtectedResourceDetails credentials() {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
details.setAccessTokenUri("https://external-service.example.com/OAuth/Token");
details.setClientId("abcdefghijklmnopq");
details.setClientSecret("123456789123456789123456789");
details.setGrantType("client_credentials");
return details;
}
@Bean
public OAuth2RestTemplate externalServiceRestTemplate() {
return new OAuth2RestTemplate(credentials(), oauth2Context);
}
}
效果很好,我可以将 OAuth2RestTemplate bean 注入到我的服务中:
@Autowired
@Qualifier("externalServiceRestTemplate")
private OAuth2RestTemplate restTemplate;
但是,在我的应用程序中,我有许多用户,每个用户都需要配置自己的客户端密钥。如果相关,我正在执行此批处理作业,这意味着它是在常规 HTTP 请求之外完成的,有时是在同一线程上下文中完成的。
这意味着我将需要多个 OAuth2ProtectedResourceDetails,并且我假设还需要多个 OAuth2RestTemplate 实例。由于这是每个用户将自行配置的内容,因此它必须根据保存在数据库中的客户端凭据动态发生。
有人对如何以高效但线程安全的方式配置动态数量的 OAuth2RestTemplate 实例有任何建议吗?
由于还没有人回复,我将尝试回答我自己的问题。
我创建了一个存储库 bean,它缓存 returns 一个基于秘密客户端密钥的 RestTemplate:
@Repository
public class ExternalServiceRepository {
private static ConcurrentHashMap<String, OAuth2RestTemplate> restTemplates = new ConcurrentHashMap<>();
/**
* Get a RestTemplate for a specific client based on it's client secret id.
* Create one if it hasn't been initialized yet.
*/
public OAuth2RestTemplate restTemplate(String clientKey) {
synchronized (restTemplates) {
OAuth2RestTemplate restTemplate = restTemplates.get(clientKey);
if (restTemplate == null) {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
details.setAccessTokenUri("https://external-service.example.com/OAuth/Token");
details.setClientId("abcdefghijklmnopq");
details.setClientSecret(clientKey);
details.setGrantType("client_credentials");
restTemplate = new OAuth2RestTemplate(details, new DefaultOAuth2ClientContext());
restTemplates.put(clientKey, restTemplate);
}
return restTemplate;
}
}
}
我没有使用为每个 HTTP 会话设置 OAuth2 客户端上下文的 @EnableOAuth2Client 注释,而是创建了我自己的 DefaultOAuth2ClientContext。由于我仅使用客户端凭据,因此我相信此代码是线程安全的(如果您不这么认为,请证明我错了)。
最后,我没有注入 RestTemplate,而是注入并使用我的存储库来访问给定客户端密钥的 restTemplate:
RestTemplate restTemplate =
externalServiceRepository.restTemplate("123456789123456789123456789");