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

如何用Spring Security OAuth2 Client实现服务间OAuth2认证

使用Spring Security OAuth2 Client实现服务到服务的OAuth2认证

核心思路

服务自身调用下游OAuth2服务,应采用客户端凭证模式(Client Credentials Grant)——这种模式无需用户参与,完全是服务间的身份认证,不会和上游传入的用户SecurityContext产生冲突。

步骤1:修正配置文件

你的配置中包含了redirectUri,这是授权码模式的参数,客户端凭证模式不需要。授权服务器收到多余参数可能返回unauthorized_client错误,修改配置如下:

downstream:
    location: "https://downstream.domain.com"
    oauth2:
        registration-id: "downstream-client"
        client-id: "你的客户端ID"
        client-secret: "你的客户端密钥" # 若未使用加密直接配置值,加密场景保留key/value结构
        tokenUri: "https://授权服务器地址/oauth2/token"
        scope: "quote.read" # 下游要求的权限范围
        # 移除redirectUri、authorizationUri,客户端凭证模式不需要这两个参数

步骤2:配置OAuth2客户端管理器

创建独立的AuthorizedClientManager,用于管理服务自身的OAuth2凭证,不依赖请求上下文的用户:

import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.service.InMemoryOAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.service.OAuth2AuthorizedClientService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OAuth2ClientConfig {

    @Bean
    public OAuth2AuthorizedClientManager downstreamAuthorizedClientManager(
            ClientRegistrationRepository clientRegistrationRepository) {
        // 用内存存储服务自身的授权客户端,不关联用户
        OAuth2AuthorizedClientService authorizedClientService = 
                new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);
        
        // 构建客户端凭证模式的授权提供器
        OAuth2AuthorizedClientProvider authorizedClientProvider =
                OAuth2AuthorizedClientProviderBuilder.builder()
                        .clientCredentials()
                        .build();
        
        AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
                new AuthorizedClientServiceOAuth2AuthorizedClientManager(
                        clientRegistrationRepository, authorizedClientService);
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
        
        return authorizedClientManager;
    }
}

步骤3:构建带OAuth2认证的HttpInterface

通过WebClient实现你的DownstreamClient,绑定上面的AuthorizedClientManager,确保自动获取/刷新Token且不干扰上游SecurityContext:

import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.support.WebClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;

@Configuration
public class DownstreamClientConfig {

    @Bean
    public DownstreamClient downstreamClient(OAuth2AuthorizedClientManager downstreamAuthorizedClientManager) {
        // 创建专属OAuth2过滤函数,使用服务自身的授权管理器
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Filter =
                new ServletOAuth2AuthorizedClientExchangeFilterFunction(downstreamAuthorizedClientManager);
        // 指定使用的客户端注册ID
        oauth2Filter.setDefaultClientRegistrationId("downstream-client");

        // 构建WebClient
        WebClient webClient = WebClient.builder()
                .baseUrl("https://downstream.domain.com")
                .filter(oauth2Filter)
                .build();

        // 生成HttpInterface代理
        HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient))
                .build();
        return factory.createClient(DownstreamClient.class);
    }
}

关键注意事项

  • 避免干扰上游SecurityContext:这里使用的AuthorizedClientServiceOAuth2AuthorizedClientManager是服务级别的存储,不绑定当前请求的用户,上游传入的用户认证信息不会被修改或覆盖。
  • @RegisteredOAuth2AuthorizedClient无效原因:该注解用于绑定当前请求用户对应的授权客户端,服务自身认证无需用户,因此不适用。
  • unauthorized_client错误排查:除了多余的redirectUri,还要检查:
    • client-id和client-secret是否与授权服务器配置一致
    • tokenUri是否正确(客户端凭证模式仅需该地址)
    • 授权服务器是否允许该客户端使用客户端凭证模式
    • scope是否为授权服务器允许该客户端访问的范围

替代方案:RestClient实现

若偏好使用RestClient,配置逻辑类似:

import org.springframework.web.client.RestClient;
import org.springframework.web.client.support.RestClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.web.client.OAuth2AuthorizedClientExchangeFilterFunction;

@Configuration
public class DownstreamClientConfig {

    @Bean
    public DownstreamClient downstreamClient(OAuth2AuthorizedClientManager downstreamAuthorizedClientManager) {
        OAuth2AuthorizedClientExchangeFilterFunction oauth2Filter =
                new OAuth2AuthorizedClientExchangeFilterFunction(downstreamAuthorizedClientManager);
        oauth2Filter.setDefaultClientRegistrationId("downstream-client");

        RestClient restClient = RestClient.builder()
                .baseUrl("https://downstream.domain.com")
                .filter(oauth2Filter)
                .build();

        HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(RestClientAdapter.for(restClient))
                .build();
        return factory.createClient(DownstreamClient.class);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 14:53:17