Spring Authorization Server能否实现未过期Token复用?
需求描述
Spring Authorization Server默认每次请求Token时都会生成唯一实例,现在需要实现:若客户端之前的Token尚未过期,则返回相同的Token;若Token已过期,则生成新的Token。该需求完全可以实现,核心是通过自定义授权流程中的关键组件,拦截并复用有效授权记录。
用户提供的原始配置代码
注册客户端配置
RegisteredClient .withId(UUID.randomUUID().toString()) .clientId("client") .clientSecret("{noop}secret") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) .clientSettings(ClientSettings.builder() .tokenEndpointAuthenticationSigningAlgorithm(SignatureAlgorithm.RS256) .build()) .tokenSettings(TokenSettings.builder() .accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED) .idTokenSignatureAlgorithm(SignatureAlgorithm.RS256) .accessTokenTimeToLive(Duration.ofMinutes(30)) .build()) .scope("read") .build();
默认内存授权服务
public OAuth2AuthorizationService authorizationService() { return new InMemoryOAuth2AuthorizationService(); }
实现方案
核心思路是在客户端凭证模式的Token生成流程中,先查询当前客户端是否存在未过期的有效授权记录,存在则复用,不存在再生成新Token。具体实现如下:
1. 自定义客户端凭证认证Provider
扩展默认的DefaultClientCredentialsAuthenticationProvider,重写授权创建逻辑,增加有效授权查询:
@Component public class ReusableClientCredentialsAuthenticationProvider extends DefaultClientCredentialsAuthenticationProvider { private final OAuth2AuthorizationService authorizationService; public ReusableClientCredentialsAuthenticationProvider(OAuth2AuthorizationService authorizationService, OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) { super(authorizationService, tokenGenerator); this.authorizationService = authorizationService; } @Override protected OAuth2Authorization createAuthorization(OAuth2ClientCredentialsAuthenticationToken authentication) { RegisteredClient registeredClient = authentication.getRegisteredClient(); String clientId = registeredClient.getId(); // 查询客户端名下未过期的授权记录 OAuth2Authorization validAuthorization = authorizationService.findByClientIdAndPrincipalName(clientId, clientId) .stream() .filter(auth -> { OAuth2AccessToken accessToken = auth.getAccessToken(); return accessToken != null && !accessToken.isExpired(); }) .findFirst() .orElse(null); if (validAuthorization != null) { // 返回已存在的有效授权,避免生成新Token return validAuthorization; } // 无有效授权时,执行默认逻辑生成新授权 return super.createAuthorization(authentication); } }
2. 配置自定义Provider到授权服务器
在Spring Security配置中替换默认的客户端凭证认证Provider:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); // 替换默认的客户端凭证认证Provider http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) .tokenEndpoint(tokenEndpoint -> tokenEndpoint .authenticationProvider(reusableClientCredentialsAuthenticationProvider())); http.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); return http.build(); } @Bean public ReusableClientCredentialsAuthenticationProvider reusableClientCredentialsAuthenticationProvider( OAuth2AuthorizationService authorizationService, OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) { return new ReusableClientCredentialsAuthenticationProvider(authorizationService, tokenGenerator); } // 注册客户端仓库、JWT解码器等其他必要Bean @Bean public RegisteredClientRepository registeredClientRepository() { RegisteredClient registeredClient = // 你的客户端配置代码 return new InMemoryRegisteredClientRepository(registeredClient); } }
注意事项
- 上述实现针对客户端凭证模式,若需支持其他授权模式(如授权码模式),需调整授权记录的查询逻辑(比如根据用户principal查询)。
- 若使用持久化的
OAuth2AuthorizationService(如JdbcOAuth2AuthorizationService),同样可以复用该逻辑,只需确保查询语句能正确过滤未过期的Token。 - 高并发场景下建议增加分布式锁或原子操作,避免同一客户端同时发起请求时重复生成Token。
内容的提问来源于stack exchange,提问作者Sard
相关产品推荐
相关产品推荐

