簡體   English   中英

如何在 Spring Boot 中使用自定義 grant_type 緩存 OAuth2RestTemplate?

[英]How to cache OAuth2RestTemplate with a cusom grant_type in Spring Boot?

我見過類似的線程,但我的不同之處在於我使用的是自定義grant type 給你一個背景知識,當我們從另一個微服務調用一個微服務時,我們使用一個delegation令牌,其中包含發起調用的用戶的詳細信息。 因此,用戶U1調用S1S1調用S2 ,以便S2U1詳細信息用於審計和許可目的。

現在為了實現這一點,我們對OAuth2RestTemplate進行了以下配置:

    @Bean(name = "delegationResource")
    @Autowired
    public OAuth2ProtectedResourceDetails delegationResource(OAuth2ClientAuthenticationSettings settings) {
        OAuth2AuthenticationSettings authSettings = authenticationSettings != null ? authenticationSettings : new OAuth2AuthenticationSettings();
        StringBuilder url = new StringBuilder();
        url.append(settings.getAuthorisationUrl() != null ? settings.getAuthorisationUrl() : authSettings.getUrl());
        url.append(settings.getAccessTokenPath());

        DelegationResourceDetails details = new DelegationResourceDetails(authenticationFacade);
        details.setClientId(settings.getClientId());
        details.setClientSecret(settings.getClientSecret());
        details.setAccessTokenUri(url.toString());
        details.setGrantType("custom");
        if(settings.getScopes() != null) {
            details.setScope(Arrays.asList(settings.getScopes()));
        }
        return details;
    }

    @Bean(name = "requestScopeClientContext")
    @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) //Without request-scope, RestTemplate is not thread-safe
    public OAuth2ClientContext requestScopeClientContext() {
        //This is used for delegation requests and needs to be scoped as request otherwise the first token will be used for all other subsequent calls regardless of what user is initiating it
        return new DefaultOAuth2ClientContext();
    }

    @Autowired
    CorrelationIDInterceptor correlationIDInterceptor;

    @Bean(name = "delegationOauth2RestTemplate")
    //if access to a third party resource is required, a new bean should be created with a @Qualifier
    @Autowired
    public OAuth2RestTemplate clientCredentialDelegationOauth2RestTemplate(@Qualifier("delegationResource") OAuth2ProtectedResourceDetails delegationResource, @Qualifier("requestScopeClientContext")  OAuth2ClientContext sessionScopeClientContext) {
        /*
        This is used to send requests from a micro-service to another on behalf of the user who initiated the original request
        so this has to have a thread-safe client-context
         */
        ArrayList<ClientHttpRequestInterceptor> clientHttpRequestInterceptors = new ArrayList<>();
        clientHttpRequestInterceptors.add(correlationIDInterceptor);
        DelegationOAuth2RestTemplate delegationOAuth2RestTemplate = new DelegationOAuth2RestTemplate(delegationResource, sessionScopeClientContext);
        delegationOAuth2RestTemplate.setInterceptors(clientHttpRequestInterceptors);
        return delegationOAuth2RestTemplate;
    }

如您所見, OAuth2ClientContext必須在request scope 中,否則將使用以前的用戶詳細信息,並且不會為第二個用戶生成令牌,依此類推。

但這會對性能產生一些影響。 當我們有許多並發用戶時,效果變得更加明顯。 因此,作為一種解決方案,我正在考慮為每個用戶緩存OAuth2ClientContext ,並將緩存到期設置為小於令牌到期的值。 盡管緩存過期並不是真正的問題,因為每個令牌都會在得到這一點之前進行驗證。

現在的問題是我如何實現這一目標,最好的方法是什么? From my understanding I need to change the scope from request to singleton like the default Spring bean's scope and then somehow make it to create a new instance when there is no entry in the cache? 不知道如何做到這一點?

好的,當我瀏覽了一些頁面並使用此解決方案測試了不同的場景時,我正在回答我的問題,所有這些都運行良好。

所以我修改了創建新OAuth2ClientContext的方法,以便從緩存的方法中獲取它:

    @Bean(name = "requestScopeClientContext")
    @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) //Without request-scope, RestTemplate is not thread-safe
    public OAuth2ClientContext requestScopeClientContext() {
        if(!authenticationSettings.getDisableCachedToken()) {
            String username = authenticationFacade.getPrincipal().getUsername();
            DefaultOAuth2ClientContext occ = cachedService.getOAuth2ClientContextByUser(username);
            logger.debug("DefaultOAuth2ClientContext is fetched with id {} for user {}", occ.hashCode(), username);
            return occ;
        } else {
            logger.debug("DefaultOAuth2ClientContext has not been cached!");
            return new DefaultOAuth2ClientContext();
        }
    }

這是緩存的方法:

@Service
public class CachedService {

    /**
     * The <code>principal</code> argument is used as an entry for the underlying cache so that
     * Spring doesn't call to issue new token if there is already available for that user.
     * The option <code>sync</code> is set to true for concurrent requests, if not true all the calls to this method will initiate cache calculation
     * before the cache being updated even if they are all the same user, setting true ensures the first call does the computation and the
     * rest will benefit from the cache.
     * @param principal
     * @return
     */
    @Cacheable(value = "delegation", sync = true, key = "#principal", cacheManager = "caffeine")
    public DefaultOAuth2ClientContext getOAuth2ClientContextByUser(String principal) {
        System.out.println("CALLED");
        //This is used for delegation requests and needs to be scoped as request otherwise the first token will be used for all other subsequent calls regardless of what user is initiating it
        return new DefaultOAuth2ClientContext();
    }
}

正如您所看到的,有一個參數principal尚未在方法本身中使用,但對於緩存來說,擁有一個返回OAuth2ClientContext正確實例的鍵很重要。

我已經用緩存的短期和長期到期測試了這一點,前者的時間短於令牌到期時間,后者的時間長於令牌的到期日期:

在這兩種情況下,如果令牌已過期或根本不可用, OAuth2ClientContext都會巧妙地請求新令牌。

所以沒有問題,就像一個魅力。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM