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

如何为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>

关键说明

  1. 初始将accessToken设为过期状态,确保WebClient首次请求时自动触发refresh_token刷新逻辑
  2. 刷新成功后会自动保存新的accessToken,若API返回新的refresh_token也会同步更新
  3. 用static-user作为固定用户标识,因为该refresh_token为全局有效,无需关联具体用户

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 09:35:24