繁体   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