代表多个用户使用OAuth2RestTemplate

时间:2016-10-28 13:50:57

标签: java spring spring-security-oauth2 resttemplate

我正在创建一个系统,该系统定期将代表许多用户的数据导出到外部系统,并通过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实例有任何建议?

1 个答案:

答案 0 :(得分:4)

由于没有人回复,我会尝试回答我自己的问题。

我创建了一个Repository bean,它根据秘密客户端密钥缓存并返回一个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;
        }
    }
}

我没有使用@ EnableOAuth2Client注释来为每个HTTP会话设置OAuth2客户端上下文,而是创建自己的DefaultOAuth2ClientContext。由于我只使用客户端凭据,因此我认为此代码是线程安全的(如果您不这么认为,请证明我错了。)

最后,我没有注入RestTemplate,而是注入并使用我的存储库来访问给定客户端密钥的restTemplate:

RestTemplate restTemplate =
    externalServiceRepository.restTemplate("123456789123456789123456789");