.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
相关产品推荐
相关产品推荐

