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

使用MSAL、OpenID Connect调用Azure受保护Web API时遇401未授权

问题:Azure受保护Web API返回401 Unauthorized,已携带Access Token

客户端应用访问部署在Azure上的受保护Web API,打开应用时已获取Access Token并添加到请求头,但API返回401 unauthorized。

客户端获取Access Token的代码

public async Task<string> GetToken()
{
    var authorizationUri = new Uri($@"https://login.microsoftonline.com/{AUTHORITY}");
    var pca = PublicClientApplicationBuilder.Create(CLIENTID)
       .WithAuthority(authorizationUri)
       //.WithAuthority(AadAuthorityAudience.AzureAdAndPersonalMicrosoftAccount)
       .WithRedirectUri(REDIRECTURL).Build();

    // 获取账户
    var accounts = await pca.GetAccountsAsync();
    var accountToLogin = PublicClientApplication.OperatingSystemAccount;
    try
    {
        // 静默获取Token
        var authResult = await pca.AcquireTokenSilent(new[] { "api://..api-id../access_as_user" }, accountToLogin) 
        .ExecuteAsync();                

        return authResult.AccessToken;
    }
    catch (MsalUiRequiredException) 
    {
        // 交互式获取Token
        var authResult = await pca.AcquireTokenInteractive(new[] { "api://..api-id../access_as_user" }) 
        .WithAccount(accountToLogin)  
        .ExecuteAsync();

        return authResult.AccessToken;
    }
}

调用Web API的代码

public async Task<string> GetForecast()
{
    var access = new ServiceAccess();
    var token = await access.GetToken();

    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    client.DefaultRequestHeaders.Authorization =
        new AuthenticationHeaderValue("Bearer", token);

    using HttpResponseMessage response = await client.GetAsync("https://my-api.azurewebsites.net/api/weatherforecast");
    response.EnsureSuccessStatusCode();
    ...
}   

Web API的认证配置

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "mypolicy",
        policy =>
        {
            policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
        });
});

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options => builder.Configuration.Bind("AzureAd", options));

已检查的Token内容(jwt.io验证无异常)

{
  "aud": "my web API id",
  "iss": "https://login.microsoftonline.com/my-tenant-id/v2.0",
  "iat": 1675018002,
  "nbf": 1675018002,
  "exp": 1675022027,
  "aio": "....",
  "azp": "my web API id",
  "azpacr": "0",
  "idp": "live.com",
  "name": "...",
  "oid": "some guid",
  "preferred_username": "...@gmail.com",
  "rh": "???",
  "scp": "access_as_user",
  "sub": "???",
  "tid": "my tenant id",
  "uti": "???",
  "ver": "2.0"
}

已排查的情况

  • 排除受众错误:Token的aud字段已包含Web API的ID
  • 已尝试官方ASP.NET Web API认证手册,但问题未解决

解决方案建议
  1. 检查Web API的AzureAd配置项
    确认appsettings.json中的AzureAd配置是否正确,核心字段需与Token匹配:

    • ClientId:必须等于Token中的aud值(Web API的ID)
    • TenantId:必须等于Token中的tid值
    • Instance:应为https://login.microsoftonline.com/
      示例正确配置:
    "AzureAd": {
      "Instance": "https://login.microsoftonline.com/",
      "TenantId": "你的租户ID",
      "ClientId": "你的Web API ID"
    }
    
  2. 显式配置Token发行方验证
    虽然Token内容正常,但Web API可能未正确验证发行方,可在配置中强制指定:

    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            builder.Configuration.Bind("AzureAd", options);
            options.TokenValidationParameters.ValidIssuer = $"https://login.microsoftonline.com/{builder.Configuration["AzureAd:TenantId"]}/v2.0";
            options.TokenValidationParameters.ValidateIssuer = true;
        });
    
  3. 确认Web API的账户访问权限
    Token中idp为live.com,说明是个人微软账户登录,需检查:

    • Azure门户中Web API的应用注册,"支持的账户类型"是否设置为"任何组织目录中的账户和个人微软账户"
    • 客户端应用已被授予access_as_user权限,且该权限已完成管理员或用户同意
  4. 检查认证中间件顺序
    确保在Program.cs中正确添加中间件,顺序不可颠倒:

    app.UseAuthentication();
    app.UseAuthorization();
    
  5. 验证控制器授权特性
    确认API控制器或方法上添加了[Authorize]特性,无错误策略配置:

    [ApiController]
    [Route("api/[controller]")]
    [Authorize]
    public class WeatherForecastController : ControllerBase
    {
        // ...
    }
    
  6. 捕获认证失败详情
    添加事件处理获取具体错误原因,便于排查:

    .AddJwtBearer(options =>
    {
        builder.Configuration.Bind("AzureAd", options);
        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                // 记录日志或输出错误信息
                Console.WriteLine($"认证失败: {context.Exception.Message}");
                return Task.CompletedTask;
            }
        };
    });
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 21:41:11