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

.NET多API间用户安全令牌复用认证问题求助

解决方案:使用Azure AD On-Behalf-Of (OBO) 流程实现跨API安全调用

你遇到的核心问题是令牌受众(Audience)不匹配:WebAPI1收到的用户令牌是针对自身Client ID的,而WebAPI2只接受受众为自己Client ID的令牌,直接复用自然会报签名无效的401错误。不用硬编码用户凭据的最优方案是采用Azure AD的OBO流程——让WebAPI1代表当前用户,向Azure AD申请一个专门针对WebAPI2的访问令牌。

一、先在Azure AD中配置权限关系

  • 打开WebAPI1的应用注册,在「API权限」中添加委托权限:搜索WebAPI2的应用注册,勾选它暴露的API范围(比如api://<WebAPI2-Client-ID>/access_as_user)
  • 确保WebAPI2的应用注册已经在「公开API」中添加了至少一个范围(如果还没加,直接创建一个即可,名称可以是access_as_user)
  • 对WebAPI1添加的WebAPI2权限授予管理员同意(如果是租户内部应用,这一步是必须的,否则用户会被要求授权,控制台应用无法处理交互式授权)

二、修改WebAPI1的配置与代码

1. 更新appsettings.json

添加WebAPI2的范围和WebAPI1自身的客户端密钥(这个是WebAPI1应用注册的密钥,不是用户凭据,后续变更只需更新配置无需重新部署):

"AzureAd": {
  "Instance": "https://login.microsoftonline.com/",
  "Domain": "<你的租户域名>",
  "TenantId": "<你的租户ID>",
  "ClientId": "<WebAPI1的Client ID>",
  "ClientSecret": "<WebAPI1的客户端密钥>",
  "WebApi2Scope": "api://<WebAPI2的Client ID>/access_as_user"
}

2. 调整Startup.cs配置

把WebAPI2的范围加入受保护API调用的配置,同时添加HttpClient工厂方便调用WebAPI2:

public void ConfigureServices(IServiceCollection services)
{
    services.AddProtectedWebApiWithMicrosoftIdentityPlatformV2(Configuration)
        // 把WebAPI2的范围加入到受保护API列表中
        .AddProtectedApiCallsWebApis(Configuration, new string[] { "user.read", Configuration["AzureAd:WebApi2Scope"] })
        .AddInMemoryTokenCaches();

    // 注册HttpClient用于调用WebAPI2
    services.AddHttpClient("WebApi2Client", client =>
    {
        client.BaseAddress = new Uri("<WebAPI2的基础URL,比如https://localhost:5001>");
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

3. 在WebAPI1的控制器中实现OBO调用

通过ITokenAcquisition服务获取针对WebAPI2的令牌,再发起请求:

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class SampleController : ControllerBase
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly ITokenAcquisition _tokenAcquisition;
    private readonly IConfiguration _configuration;

    public SampleController(IHttpClientFactory httpClientFactory, ITokenAcquisition tokenAcquisition, IConfiguration configuration)
    {
        _httpClientFactory = httpClientFactory;
        _tokenAcquisition = tokenAcquisition;
        _configuration = configuration;
    }

    [HttpGet]
    public async Task<IActionResult> GetCombinedData()
    {
        // 1. 通过OBO流程获取针对WebAPI2的访问令牌
        var webApi2Scope = _configuration["AzureAd:WebApi2Scope"];
        var webApi2Token = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { webApi2Scope });

        // 2. 调用WebAPI2
        var webApi2Client = _httpClientFactory.CreateClient("WebApi2Client");
        webApi2Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", webApi2Token);

        var webApi2Response = await webApi2Client.GetAsync("/api/your-resource-endpoint");
        if (!webApi2Response.IsSuccessStatusCode)
        {
            return StatusCode((int)webApi2Response.StatusCode, "调用WebAPI2失败");
        }

        var webApi2Data = await webApi2Response.Content.ReadAsStringAsync();
        // 结合WebAPI1自身的数据返回
        return Ok(new 
        { 
            WebApi1Data = "从WebAPI1获取的基础数据", 
            WebApi2Data = webApi2Data 
        });
    }
}

三、验证WebAPI2的配置

确保WebAPI2的Startup.cs和appsettings.json正确配置了Azure AD认证,受众指向自身Client ID:

WebAPI2的appsettings.json

"AzureAd": {
  "Instance": "https://login.microsoftonline.com/",
  "Domain": "<你的租户域名>",
  "TenantId": "<你的租户ID>",
  "ClientId": "<WebAPI2的Client ID>"
}

WebAPI2的Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddProtectedWebApiWithMicrosoftIdentityPlatformV2(Configuration);
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseHttpsRedirection();
    app.UseAuthentication();
    app.UseMvc();
}

关键说明

  • 为什么之前调用Graph成功?:因为你在AddProtectedApiCallsWebApis中添加了Graph的user.read范围,OBO流程会自动处理针对Graph的令牌申请;而WebAPI2的范围没配置,所以无法获取正确的令牌。
  • 令牌缓存:AddInMemoryTokenCaches()会自动缓存获取到的WebAPI2令牌,避免每次请求都向Azure AD申请,提升性能。
  • 客户端密钥的安全性:WebAPI1的客户端密钥是应用级别的凭据,不是用户凭据,你可以通过Azure Key Vault来存储它,进一步提升安全性(部署到Azure后更推荐这种方式)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:02:52