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

Spring OAuth2客户端凭证流:动态自定义Audience与Scope问题

场景
  • Auth0应用(client-id/secret)internal-services
    • 已授权调用apiA(受众:http//apiA)
    • 已授权调用apiB(受众:http//apiB)
  • 需要调用apiA和apiB的单体应用
  • 微服务A(受众:http//apiA)
  • 微服务B(受众:http//apiB)

Spring单体应用配置

spring:
  security:
    oauth2:
      client:
        registration:
          auth0:
            client-id: bla
            client-secret: bla
            authorization-grant-type: client_credentials

        provider:
          auth0:
            issuer-uri: https://bla.au.auth0.com/

最初的实现思路

private OAuth2AccessToken getAccessToken(String audience, String scope) {
  OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
      .withClientRegistrationId("auth0")
      .principal("monolith")
      .attributes(attrs -> {
          attrs.put("scope", scope);
          attrs.put("audience", audience);
        }
      )
      .build();

  OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);

  OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
  return accessToken;
}

public void doWorkA() {
  callApiA(
    data,
    getAccessToken("http//apiA", "customScopeA")
  );
}

public void doWorkB() {
  callApiB(
    data,
    getAccessToken("http//apiB", "customScopeB")
  );
}

官方文档中的标准配置方式

@Bean
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient() {
  RestClientClientCredentialsTokenResponseClient client = new RestClientClientCredentialsTokenResponseClient();
  client.addParametersConverter(grantRequest -> {
    MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
    parameters.set(OAuth2ParameterNames.AUDIENCE, "???");
    parameters.set(OAuth2ParameterNames.SCOPE, "???");
    return parameters;
  });
  return client;
}

遇到的问题

注册的ParameterConverter无法获取原始OAuth2AuthorizeRequest中的属性,缺少上下文信息。

疑问

  1. 是否有官方文档记载的方法可以实现需求?
  2. 接受更务实的实现方式(比如在application.yml中重复配置,但无法在此指定audience)。
  3. Gemini建议复制authorize方法实现自定义ClientCredentialsOAuth2AuthorizedClientProvider,但这种方式太繁琐。

希望存在简单直接的解决方案,只是自己没发现。

临时解决方案

  • 该方案绕过了OAuth2AuthorizedClientManager.authorize()的预期流程
  • 会绕过框架的缓存等实用功能

使用示例

@Autowired
private OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> tokenResponseClient;

Map<String, Object> additionalParameters = new HashMap<>();
additionalParameters.put("audience", microserviceAudience);

// 获取Auth0 M2M应用的客户端注册信息
ClientRegistration clientRegistration = clientRegistrationRepository.findByRegistrationId("auth0");

// 创建自定义授权请求,传入额外参数
CustomClientCredentialsGrantRequest grantRequest =
new CustomClientCredentialsGrantRequest(clientRegistration, additionalParameters);

// 使用配置的客户端执行客户端凭证流程
OAuth2AccessTokenResponse tokenResponse = tokenResponseClient.getTokenResponse(grantRequest);

OAuth2AccessToken accessToken = tokenResponse.getAccessToken();

配置类

@Bean
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient() {
  DefaultClientCredentialsTokenResponseClient tokenResponseClient =
      new DefaultClientCredentialsTokenResponseClient();

  // 使用自定义令牌请求增强器添加自定义参数
  tokenResponseClient.setRequestEntityConverter(new CustomRequestEntityConverter());

  return tokenResponseClient;
}

转换器实现

public RequestEntity<?> convert(OAuth2ClientCredentialsGrantRequest grantRequest) {
  // 使用默认转换器获取标准RequestEntity
  RequestEntity<?> entity = defaultConverter.convert(grantRequest);
  if (entity == null) {
    return null;
  }

  // 如果是自定义请求类型,添加额外参数
  if (grantRequest instanceof CustomClientCredentialsGrantRequest) {
    CustomClientCredentialsGrantRequest customRequest = (CustomClientCredentialsGrantRequest) grantRequest;

    ...创建包含自定义参数的新RequestEntity...
  }
  return entity;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 18:05:18