Azure AD:在WebAPI中切换身份以调用下游服务
解决WebAPI以服务身份调用另一WebAPI的ADAL.NET实现方案
我刚好之前处理过完全相同的Azure AD场景,用ADAL.NET实现服务到服务的身份验证核心是切换到客户端凭据流(Client Credentials Flow)——这是专门给服务账户用的无用户交互认证方式,和用户委托的令牌完全独立。下面给你一步步讲清楚配置和代码实现:
第一步:Azure AD后台配置(关键!很多人在这里踩坑)
- 给WebAPI2的应用注册暴露API范围:在WebAPI2的应用注册里,找到“公开API”,添加一个应用类型的范围(比如
access_as_app),别选委托类型的范围。 - 给WebAPI1的应用注册添加应用权限:在WebAPI1的应用注册里,找到“API权限”,点击“添加权限”→选择WebAPI2→勾选刚才创建的应用权限,最后一定要点击“授予管理员同意”(这个步骤不能少,否则令牌会报权限不足)。
- 给WebAPI1生成客户端凭据:在WebAPI1的应用注册里,找到“证书和密码”,添加一个客户端密码(生产环境更推荐用证书),保存好密码值(只显示一次)。
第二步:WebAPI1中用ADAL.NET获取服务令牌
在需要调用WebAPI2的方法里,用客户端凭据流获取服务账户的令牌,代码示例如下:
using Microsoft.IdentityModel.Clients.ActiveDirectory; using System.Threading.Tasks; public class WebApi2ServiceClient { private readonly string _tenantId; private readonly string _webApi1ClientId; private readonly string _webApi1ClientSecret; private readonly string _webApi2ResourceId; // 建议从配置文件读取这些参数,绝对不要硬编码 public WebApi2ServiceClient(IConfiguration config) { _tenantId = config["AzureAd:TenantId"]; _webApi1ClientId = config["AzureAd:ClientId"]; _webApi1ClientSecret = config["AzureAd:ClientSecret"]; // WebAPI2的资源ID可以是它的应用ID URI,比如api://<webapi2-client-id>/.default _webApi2ResourceId = config["WebApi2:ResourceId"]; } public async Task<string> GetServiceAccessToken() { var authContext = new AuthenticationContext($"https://login.microsoftonline.com/{_tenantId}"); var clientCredential = new ClientCredential(_webApi1ClientId, _webApi1ClientSecret); // 获取服务账户的访问令牌 var authResult = await authContext.AcquireTokenAsync(_webApi2ResourceId, clientCredential); return authResult.AccessToken; } // 用获取到的令牌调用WebAPI2的示例方法 public async Task CallWebApi2() { string accessToken = await GetServiceAccessToken(); using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); var response = await httpClient.GetAsync("https://your-webapi2-url/api/your-endpoint"); response.EnsureSuccessStatusCode(); // 这里添加你的响应处理逻辑 } }
第三步:WebAPI2端的授权验证配置
确保WebAPI2的Startup.cs里配置了正确的JWT验证,允许应用权限的令牌:
public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = "https://login.microsoftonline.com/your-tenant-id"; options.Audience = "api://your-webapi2-client-id"; // WebAPI2的应用ID URI // 验证令牌的发行者和受众 options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = "https://sts.windows.net/your-tenant-id/", ValidateAudience = true, ValidAudience = "api://your-webapi2-client-id" }; }); // 配置授权策略,只允许指定服务账户访问 services.AddAuthorization(options => { options.AddPolicy("ServiceAccess", policy => { policy.RequireClaim("azp", "webapi1-client-id"); // 限定只有WebAPI1的服务账户能访问 // 或者检查权限范围:policy.RequireClaim("scp", "access_as_app"); }); }); }
常见坑点提醒
- 绝对不要把客户端密钥硬编码到代码里,生产环境建议用Azure Key Vault存储,再通过配置读取。
- 必须给WebAPI1的应用权限授予管理员同意,否则获取令牌时会返回
invalid_grant错误。 - 如果用证书代替客户端密钥,把
ClientCredential换成ClientAssertionCertificate类,安全性更高。
内容的提问来源于stack exchange,提问作者cty
相关产品推荐
相关产品推荐

