如何在Spring Security OAuth2Client中缓存获取到的JWT令牌
问题根源
令牌没有被缓存的核心原因是自定义AuthorizedClientServiceOAuth2AuthorizedClientManager时漏了两个关键配置:
- 构造
OAuth2AuthorizedClientProvider时只加了最基础的客户端凭证授权逻辑,没有配置令牌过期校验、提前刷新的规则 - 没有给Manager绑定固定的主体映射规则,导致每次调用
authorize()时,Spring Security都认为是全新的主体发起的授权请求,直接跳过已存储令牌的查询逻辑,每次都向Keycloak申请新JWT。
修复方案
只需要修改ClientRegistrationConfiguration中AuthorizedClientServiceOAuth2AuthorizedClientManager的Bean配置即可,单实例场景下不需要额外引入第三方缓存组件,你当前配置的InMemoryOAuth2AuthorizedClientService本身就支持令牌存储能力。
修改后的代码如下:
@Bean public AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientServiceOAuth2AuthorizedClientManager( ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientService authorizedClientService) { OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder() .clientCredentials(clientCredConfig -> { // 令牌过期前60秒自动提前刷新,避免网络波动导致使用过期令牌 clientCredConfig.clockSkew(Duration.ofSeconds(60)); }) .refreshToken() .build(); AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientService); authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); // 固定主体名称,保证缓存key(客户端ID+主体名)全局一致,这是命中缓存的核心配置 authorizedClientManager.setContextAttributesMapper(authorizeRequest -> Collections.singletonMap(OAuth2AuthorizationContext.PRINCIPAL_NAME_KEY, "Keycloak") ); return authorizedClientManager; }
效果说明
- 配置生效后,除了第一次启动时的令牌申请、令牌到达过期窗口前的刷新请求,其余所有调用都会直接返回内存中缓存的有效JWT,不会重复向Keycloak发起令牌请求,令牌请求量会降到每个令牌有效期仅1-2次。
- 你现有的SecurityConfig、RequestConfiguration代码不需要做任何调整,修改完Manager的Bean直接生效。
- 如果是多实例部署需要全局共享令牌,只需要把
InMemoryOAuth2AuthorizedClientService替换为基于Redis等分布式缓存的OAuth2AuthorizedClientService实现即可,Manager层配置不需要改动。
内容的提问来源于stack exchange,提问作者npriebe
相关产品推荐
相关产品推荐

