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

如何避免WebClient每次请求都获取新的Access Token

问题场景

当前我们的WebClient采用OAuth2客户端凭证模式(Client Credentials)配置,在批量触发场景下(每分钟数千次请求),频繁向授权服务器请求Access Token,导致对方触发限流。需要实现首次获取Token后的缓存机制,并根据Token的有效时长自动复用,避免重复请求。

现有配置

application.yml

security:
  oauth2:
    client:
      registration:
        custom-client:
          authorization-grant-type: client_credentials
          client-authentication-method: client_secret_post
          client-id: somedummyid
          client-secret: somedummysecret
          provider: custom-client
      provider:
        custom-client:
          token-uri: xxxxxxxxxx/token

WebClient配置类

@Configuration
public class WebClientConfig {

    @Bean
    public OAuth2AuthorizedClientManager authClientManager (ClientRegistrationRepository clientRegistrationRepository,
                                                           OAuth2AuthorizedClientService authorizedClientService,
                                                           HttpComponentsClientHttpRequestFactory requestFactory) {

        RestTemplate restTemplate = new RestTemplate(
            Arrays.asList(new FormHttpMessageConverter(), new OAuth2AccessTokenResponseHttpMessageConverter()));

        restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
        restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(requestFactory));

        final DefaultClientCredentialsTokenResponseClient tokenResponseClient = new DefaultClientCredentialsTokenResponseClient();
        tokenResponseClient.setRestOperations(restTemplate);
        final ClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider();
        authorizedClientProvider.setAccessTokenResponseClient(tokenResponseClient);
        final AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
            new AuthorizedClientServiceOAuth2AuthorizedClientManager(
                clientRegistrationRepository, authorizedClientService);

        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }

    @Bean
    public WebClient customWebClient(@Qualifier("authClientManager") OAuth2AuthorizedClientManager authorizedClientManager,
                                     @Qualifier("requestReactorFactory") ReactorClientHttpConnector connector,
                                     @Value("${baseUrl}") String baseUrl) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oAuth2Filter = new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        oAuth2Filter.setDefaultClientRegistrationId("custom-client");

        return WebClient.builder()
            .clientConnector(connector)
            .filter(oAuth2Filter)
            .baseUrl(baseUrl)
            .build();

    }
}

业务调用类

@Service
public class CustomService {

    private final WebClient webClient;

    public CustomService(@Qualifier("customWebClient") WebClient webClient) {
        this.webClient = webClient;
    }


    public Object execute() {
        return webClient.post()
            .uri("xxxxx")
            .bodyValue(xxxx)
            .retrieve()
            .bodyToMono(xxx.class)
            .block();
    }

}

解决方案

Spring Security OAuth2的Client Credentials模式默认已经内置Token缓存复用机制,核心是通过OAuth2AuthorizedClientService存储已授权的客户端信息(包含未过期的Token),只有当Token过期时才会重新请求。如果当前仍出现频繁请求Token的情况,可按以下步骤优化:

1. 确保Token缓存机制正常生效

关键配置确认

  • AuthorizedClientServiceOAuth2AuthorizedClientManager:默认会将获取到的Token存入OAuth2AuthorizedClientService,默认实现是InMemoryOAuth2AuthorizedClientService(内存存储,适合单实例应用)。
  • ClientCredentialsOAuth2AuthorizedClientProvider:会自动检查Token的过期时间(基于Token返回的expires_in字段),只有Token即将过期或已过期时,才会触发新的Token请求。

配置微调:添加Token过期提前刷新

如果希望在Token过期前提前刷新(避免刚好过期时的请求失败),可以给ClientCredentialsOAuth2AuthorizedClientProvider设置时钟偏移:

final ClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider();
// 设置提前30秒刷新Token,避免过期瞬间的请求问题
authorizedClientProvider.setClockSkew(Duration.ofSeconds(30));
authorizedClientProvider.setAccessTokenResponseClient(tokenResponseClient);

2. 分布式场景下的Token缓存(可选)

如果是多实例部署的应用,内存存储的Token无法在实例间共享,会导致每个实例都请求自己的Token。此时可以替换OAuth2AuthorizedClientService为Redis实现:

步骤1:引入Spring Data Redis依赖

<!-- Maven依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

步骤2:配置Redis版OAuth2AuthorizedClientService

@Bean
public OAuth2AuthorizedClientService redisOAuth2AuthorizedClientService(
        ClientRegistrationRepository clientRegistrationRepository,
        RedisConnectionFactory redisConnectionFactory) {
    // 使用Redis存储已授权客户端信息,实现多实例Token共享
    return new RedisOAuth2AuthorizedClientService(
            clientRegistrationRepository,
            new JdkSerializationRedisSerializer(),
            redisConnectionFactory
    );
}

3. 验证Token复用效果

可以通过以下方式验证:

  • 开启授权服务器的访问日志,观察Token请求频率是否降低
  • 在代码中添加日志,打印每次请求使用的Token值,确认同一Token被多次复用

内容的提问来源于stack exchange,提问作者mwhere

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 01:58:18