如何为Spring WebClient配置基于OAuth2 refresh_token的过滤器
解决方案:用固定refresh_token为WebClient配置OAuth2认证
核心思路
由于目标API仅提供长期有效refresh_token、不支持client_credentials授权类型,我们需要手动初始化授权客户端,让WebClient自动通过refresh_token刷新获取access_token。
步骤1:自定义授权客户端管理器
创建类处理refresh_token的授权逻辑,初始化并维护授权客户端信息:
import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; import org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.web.ReactiveOAuth2AuthorizedClientManager; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2AuthorizationGrantType; import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; import org.springframework.web.reactive.function.client.WebClient; import java.time.Instant; import java.util.Map; @Component public class RefreshTokenAuthManagerProvider { private final ReactiveClientRegistrationRepository clientRegistrationRepo; private final String fixedRefreshToken; public RefreshTokenAuthManagerProvider(ReactiveClientRegistrationRepository clientRegistrationRepo, @Value("${application.external-api.refresh_token}") String fixedRefreshToken) { this.clientRegistrationRepo = clientRegistrationRepo; this.fixedRefreshToken = fixedRefreshToken; } public ReactiveOAuth2AuthorizedClientManager getAuthManager() { // 用内存存储授权客户端信息 InMemoryReactiveOAuth2AuthorizedClientService authClientService = new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepo); // 初始化授权客户端:存入给定的refresh_token,初始accessToken设为过期状态触发自动刷新 clientRegistrationRepo.findByRegistrationId("external-api") .flatMap(registration -> { OAuth2AccessToken expiredToken = new OAuth2AccessToken( OAuth2AccessToken.TokenType.BEARER, "dummy-token", Instant.now().minusSeconds(3600), Instant.now().minusSeconds(1) ); OAuth2AuthorizedClient initClient = new OAuth2AuthorizedClient( registration, "static-user", // 固定用户标识,因refresh_token为全局有效 expiredToken, fixedRefreshToken ); return authClientService.saveAuthorizedClient(initClient, null); }).block(); DefaultReactiveOAuth2AuthorizedClientManager authManager = new DefaultReactiveOAuth2AuthorizedClientManager(clientRegistrationRepo, authClientService); // 配置refresh_token授权逻辑 authManager.setAuthorizedClientProvider(context -> { if (!OAuth2AuthorizationGrantType.REFRESH_TOKEN.equals(context.getClientRegistration().getAuthorizationGrantType())) { return Mono.empty(); } return clientRegistrationRepo.findByRegistrationId(context.getClientRegistration().getRegistrationId()) .flatMap(registration -> { // 调用API的token端点刷新accessToken return WebClient.create() .post() .uri(registration.getProviderDetails().getTokenUri()) .headers(headers -> { headers.setBasicAuth(registration.getClientId(), registration.getClientSecret()); }) .bodyValue(Map.of( OAuth2ParameterNames.GRANT_TYPE, OAuth2AuthorizationGrantType.REFRESH_TOKEN.getValue(), OAuth2ParameterNames.REFRESH_TOKEN, fixedRefreshToken )) .retrieve() .bodyToMono(OAuth2AccessTokenResponse.class) .map(tokenResp -> { OAuth2AccessToken newToken = tokenResp.getAccessToken(); // 若API返回新的refresh_token则更新,否则沿用原token String newRefreshToken = tokenResp.getRefreshToken() != null ? tokenResp.getRefreshToken().getTokenValue() : fixedRefreshToken; OAuth2AuthorizedClient updatedClient = new OAuth2AuthorizedClient( registration, "static-user", newToken, newRefreshToken ); // 保存更新后的授权客户端信息 authClientService.saveAuthorizedClient(updatedClient, null).block(); return updatedClient; }); }); }); return authManager; } }
步骤2:修改WebClient配置
使用自定义授权管理器配置OAuth2过滤器:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction; import org.springframework.web.reactive.function.client.WebClient; @Configuration public class WebClientConfig { @Autowired private RefreshTokenAuthManagerProvider authManagerProvider; @Bean public WebClient externalApiWebClient() { ServerOAuth2AuthorizedClientExchangeFilterFunction oauthFilter = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authManagerProvider.getAuthManager()); oauthFilter.setDefaultClientRegistrationId("external-api"); return WebClient.builder() .baseUrl("https://api.external-api.com") .filter(oauthFilter) .build(); } }
步骤3:完善配置文件
补充客户端ID、密钥及refresh_token配置:
spring: security: oauth2: client: registration: external-api: client-id: <你的客户端ID> client-secret: <你的客户端密钥> authorization-grant-type: refresh_token provider: external-api: token-uri: https://api.external-api.com/oauth2/token application: external-api: refresh_token: <给定的长期有效refresh_token>
关键说明
- 初始将accessToken设为过期状态,确保WebClient首次请求时自动触发refresh_token刷新逻辑
- 刷新成功后会自动保存新的accessToken,若API返回新的refresh_token也会同步更新
- 用
static-user作为固定用户标识,因为该refresh_token为全局有效,无需关联具体用户
内容的提问来源于stack exchange,提问作者Sickan90
相关产品推荐
相关产品推荐

