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

.NET Core API授权策略持续返回403问题排查

排查ASP.NET Core 2 API Azure OIDC授权403问题的分步方案

我碰到过好几个类似的场景,咱们一步步来拆解排查,先从最核心的环节入手:

1. 先确认Azure AD Token里的实际声明内容

这是最关键的第一步——别猜,直接看Token里到底有什么。你可以做两件事:

  • 用JWT解析工具(比如本地的JWT.io,或者在代码里打日志)把Token解码,查看所有声明字段。重点看有没有roles或者你自定义的权限声明,注意声明的类型名称(比如Azure AD默认的角色声明是http://schemas.microsoft.com/ws/2008/06/identity/claims/role,而不是简单的role)。
  • 在OnTokenValidated事件里添加日志,打印所有声明:
OnTokenValidated = context =>
{
    foreach (var claim in context.Principal.Claims)
    {
        // 用你项目里的日志组件输出,比如_logger.LogInformation
        Console.WriteLine($"Claim Type: {claim.Type}, Value: {claim.Value}");
    }
    return Task.CompletedTask;
}

这能帮你确认Azure返回的Token里到底有没有你需要的声明,以及它们的格式是否符合预期。

2. 检查授权策略的定义是否匹配声明

打开Startup.cs里的授权策略配置,比如你定义的"View Roles"策略:

  • 如果是基于角色的策略:services.AddAuthorization(options => { options.AddPolicy("View Roles", p => p.RequireRole("DefaultRole")); }),要确认用户的角色声明类型是ClaimTypes.Role,且值和策略要求的完全一致(大小写敏感!)。
  • 如果是基于自定义声明的策略:p => p.RequireClaim("permission", "view_roles"),要确认声明的类型和值和你在代码里添加的完全匹配。

3. 验证ASP.NET Identity的角色同步逻辑

你提到用户会自动创建并分配默认角色,要确认:

  • 数据库里AspNetUserRoles表是否正确关联了用户和角色?
  • 角色对应的AspNetRoles表是否存在该默认角色?
  • 最重要的:在用户登录后,这些角色有没有被添加到ClaimsPrincipal中?默认情况下,ASP.NET Identity不会自动把数据库里的角色同步到OIDC的ClaimsPrincipal里,你需要手动在OnTokenValidated事件里从数据库拉取角色并添加:
OnTokenValidated = async context =>
{
    var userManager = context.HttpContext.RequestServices.GetRequiredService<UserManager<ApplicationUser>>();
    var email = context.Principal.FindFirstValue(ClaimTypes.Email);
    var user = await userManager.FindByEmailAsync(email);
    
    if (user != null)
    {
        // 从数据库获取用户角色
        var roles = await userManager.GetRolesAsync(user);
        var roleClaims = roles.Select(r => new Claim(ClaimTypes.Role, r)).ToList();
        
        // 添加到当前用户的身份信息中
        var identity = new ClaimsIdentity(roleClaims);
        context.Principal.AddIdentity(identity);
        
        // 必须更新HttpContext.User,否则修改不会生效!
        context.HttpContext.User = context.Principal;
    }
    return Task.CompletedTask;
}

这里容易踩的坑是忘记更新HttpContext.User,导致修改后的ClaimsPrincipal没有被后续的授权中间件使用。

4. 修正.NET Core 2.x的Claim映射问题

.NET Core 2.x对JWT声明的默认映射和后续版本不同,Azure AD返回的roles声明可能会被错误映射。你可以在Startup.cs的ConfigureServices里添加以下代码,移除默认的Claim映射,确保Azure的角色声明被正确识别:

// 移除默认的JWT声明映射,避免Azure AD的roles被错误转换
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("roles");

// 在AddOpenIdConnect里配置Claim映射,把Azure的roles映射到ClaimTypes.Role
services.AddOpenIdConnect(options =>
{
    // 其他配置...
    options.ClaimActions.MapJsonKey(ClaimTypes.Role, "roles");
});

如果Azure返回的roles是数组类型,这个映射会自动把数组里的每个角色转换成单独的Claim。

5. 检查中间件顺序是否正确

在Startup.cs的Configure方法里,中间件的顺序直接影响授权是否生效,必须保证:

app.UseAuthentication(); // 先认证
app.UseAuthorization();  // 再授权
app.UseMvc();            // 最后是MVC路由

如果UseAuthorization在UseAuthentication之前,授权中间件会拿不到认证后的用户信息,直接返回403。

6. 验证控制器的授权属性是否正确

检查你的API控制器上的授权属性,确保拼写正确(策略名称大小写敏感):

[Authorize(Policy = "View Roles")] // 注意策略名称和你定义的完全一致
[Route("api/roles")]
public class RolesController : ControllerBase
{
    // ...
}

别不小心写成了[Authorize(Roles = "View Roles")],这是基于角色的授权,和策略授权是两回事。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:39:32