ASP.NET Core Razor Pages中AddNegotiate角色授权失效问题
ASP.NET Core Razor Pages应用通过.AddNegotiate()实现Windows认证,利用ClaimsTransformer根据AD组分配角色(如"Admin"),并配置了"SiteAuth"授权策略。当前遇到以下异常情况:
- 使用
[Authorize(Policy = "SiteAuth")]对Razor页面授权正常 - 使用
[Authorize(Roles = "Admin")]授权失效,会重定向到未授权页面 - 仅使用
[Authorize]可正常授权 if (User.HasClaim(claim => claim.Value == "Admin"))判断能正常生效
断点调试确认ClaimsTransformer已执行,角色Claim已添加,调试输出的Claims中也包含"Admin"角色,且无拼写错误。
问题核心在于WindowsIdentity的特殊性:当使用[Authorize(Roles = "...")]时,授权系统会调用User.IsInRole()方法,而WindowsIdentity的IsInRole()是直接与AD交互验证用户所属组,不会读取我们通过ClaimsTransformer添加的Role Claim。即使Claims集合中存在对应的Role声明,IsInRole()仍会返回false,导致授权失效。
而User.HasClaim()和自定义策略SiteAuth直接检查Claims集合,因此可以正常生效;仅[Authorize]只验证用户是否已认证,也不受影响。
在ClaimsTransformer中,不要直接修改原有的WindowsIdentity,而是创建一个新的ClaimsIdentity,复制原身份的所有声明,再添加自定义角色声明,最后将新身份添加到ClaimsPrincipal中。这样授权系统会使用新身份的IsInRole()方法,该方法会读取Claims集合中的Role声明。
修改后的ClaimsTransformer代码如下:
public class ClaimsTransformer : IClaimsTransformation { private readonly SiteSettings _siteSettings; private readonly ILogger<ClaimsTransformer> _logger; public ClaimsTransformer(SiteSettings siteSettings, ILogger<ClaimsTransformer> logger) { _siteSettings = siteSettings; _logger = logger; } public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal) { // 避免重复添加自定义身份 if (principal.Identities.Any(id => id.AuthenticationType == "CustomRoleIdentity")) { return Task.FromResult(principal); } var wi = (WindowsIdentity)principal.Identity; var groups = wi.Groups; // 创建新的ClaimsIdentity,复制原有声明并指定角色声明类型 var customIdentity = new ClaimsIdentity( principal.Identity.Claims, "CustomRoleIdentity", principal.Identity.NameClaimType, ClaimTypes.Role ); if (groups != null) { foreach (var group in groups) { try { string groupName = string.Empty; var ntAccount = group.Translate(typeof(NTAccount)).ToString(); if(_siteSettings.AdminGroups.TryGetValue(ntAccount, out groupName)) { if(!customIdentity.HasClaim(c => c.Value == groupName)) { _logger.LogInformation($"Adding role {groupName} to claims."); var claim = new Claim(ClaimTypes.Role, groupName); customIdentity.AddClaim(claim); } } } catch (Exception ex) { _logger.LogError(ex, "Error processing AD group"); throw; } } } // 将新身份添加到Principal中 var newPrincipal = new ClaimsPrincipal(customIdentity); return Task.FromResult(newPrincipal); } }
若希望保留原WindowsIdentity,也可选择将新身份附加到原Principal:
// 保留原身份,添加新身份 var newPrincipal = new ClaimsPrincipal(principal); newPrincipal.AddIdentity(customIdentity); return Task.FromResult(newPrincipal);
修改完成后可通过以下方式确认修复效果:
- 在页面中调用
User.IsInRole("Admin"),应返回true - 使用
[Authorize(Roles = "Admin")]标记页面,授权可正常通过 - 访问
/debug-claims接口,可看到新身份中的Role声明
内容的提问来源于stack exchange,提问作者Jon P

