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

IdentityServer4与MVC客户端多角色授权失败问题求助

解决IdentityServer4 + MVC客户端多角色授权失败问题

从你提供的Claims信息来看,问题的核心很明确:你的用户角色被打包成了一个数组形式的单个Claim(role: ["ABASTECIMENTO","CANCELAMENTO"]),而ASP.NET的角色授权系统期望的是多个独立的role类型Claim(每个角色对应一个Claim)。下面是具体的解决步骤:

1. 修复IdentityServer端的角色Claim生成

先看你的ProfileService.cs,里面有一行多余的代码会导致角色Claim异常:

// 这行代码会将user.role的原始值(可能是数组)作为单个Claim添加,要移除它
claims.Add(new Claim("role", user.role.ToString()));

你已经通过_userManager.GetRolesAsync(user)正确获取了用户的所有角色,并循环添加了独立的JwtClaimTypes.Role类型Claim(JwtClaimTypes.Role本质就是字符串"role"),所以上面那行完全是多余的,会干扰最终的Claim结构。

修改后的ProfileService.cs关键部分:

public async Task GetProfileDataAsync(ProfileDataRequestContext context) { 
    var sub = context.Subject.GetSubjectId(); 
    var user = await _userManager.FindByIdAsync(sub); 
    var principal = await _claimsFactory.CreateAsync(user); 
    var claims = principal.Claims.ToList(); 
    claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList(); 

    // 保留其他自定义Claim,移除多余的role添加
    claims.Add(new Claim("id_user", user.Id.ToString())); 
    claims.Add(new Claim("name", user.Name)); 
    claims.Add(new Claim("email", user.Email.ToString())); 

    if (_userManager.SupportsUserRole) { 
        var roles = await _userManager.GetRolesAsync(user); 
        foreach (var roleName in roles) { 
            claims.Add(new Claim(JwtClaimTypes.Role, roleName)); 
            if (_roleManager.SupportsRoleClaims) { 
                var role = await _roleManager.FindByNameAsync(roleName); 
                if (role != null) claims.AddRange(await _roleManager.GetClaimsAsync(role)); 
            } 
        } 
    } 
    context.IssuedClaims = claims; 
}

2. 修复MVC客户端的Claim映射配置

你的MVC客户端使用了MapUniqueJsonKey来处理角色Claim,这个方法的作用是将JSON数组合并为单个Claim,这正是导致问题的直接原因:

options.ClaimActions.MapUniqueJsonKey("role", "role", "role");

需要把它替换为MapJsonKey,这个方法会自动遍历JSON数组,为每个元素创建独立的Claim:

修改后的Startup.cs关键部分:

services.AddAuthentication(options => { 
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; 
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; 
}) 
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme) 
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => { 
    options.Authority = Configuration.GetSection("ids_URI").Value; 
    options.ClientId = "mvc"; 
    options.ClientSecret = "secret"; 
    options.ResponseType = OpenIdConnectResponseType.Code; 
    options.GetClaimsFromUserInfoEndpoint = true; 
    options.Scope.Add("openid"); 
    options.Scope.Add("profile"); 
    options.Scope.Add("email"); 
    options.Scope.Add("role"); 
    options.SaveTokens = true; 
    options.ClaimActions.DeleteClaim("sid"); 
    options.ClaimActions.DeleteClaim("idp"); 
    // 替换为MapJsonKey,拆分数组为多个role Claim
    options.ClaimActions.MapJsonKey("role", "role"); 
    options.TokenValidationParameters.RoleClaimType = "role"; 
});

验证修改效果

完成上述修改后,重新生成Token并查看用户Claims,应该会看到两个独立的role Claim:

[8] [Claim]:{role: ABASTECIMENTO}
[9] [Claim]:{role: CANCELAMENTO}

此时ASP.NET的角色授权系统就能正确识别用户的多个角色,访问CancelamentoController和AbastecimentoController都不会再出现"访问拒绝"的提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 10:17:44