如何避免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
相关产品推荐
相关产品推荐

