You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Authorization Server能否实现未过期Token复用?

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 08:54:55