ASP.NET Core 6中如何实现基于用户/角色声明的授权机制?
解决方案
1. 修正授权策略逻辑
你当前的策略误用了RequireRole,而实际需要验证的是权限声明(而非角色),因此需要将策略改为检查指定类型的声明:
options.AddPolicy(AppPermissions.Root_Users_Create, policy => policy.RequireClaim(AppPermissions.Root_Users_Create));
这个策略会验证用户的ClaimsPrincipal中是否存在类型为AppPermissions.Root_Users_Create的声明,不管该声明是直接分配给用户,还是通过角色继承而来。
2. 让Identity自动加载角色声明到用户上下文
默认ASP.NET Core Identity不会自动将角色的声明合并到用户的ClaimsPrincipal中,需要自定义UserClaimsPrincipalFactory来实现这一点:
自定义Claims工厂类
public class CustomUserClaimsPrincipalFactory<TUser, TRole> : UserClaimsPrincipalFactory<TUser, TRole> where TUser : class where TRole : class { private readonly RoleManager<TRole> _roleManager; public CustomUserClaimsPrincipalFactory( UserManager<TUser> userManager, RoleManager<TRole> roleManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor) { _roleManager = roleManager; } protected override async Task<ClaimsIdentity> GenerateClaimsAsync(TUser user) { // 先获取用户自身的声明 var identity = await base.GenerateClaimsAsync(user); // 获取用户所属的所有角色 var userRoles = await UserManager.GetRolesAsync(user); foreach (var roleName in userRoles) { var role = await _roleManager.FindByNameAsync(roleName); if (role == null) continue; // 将角色的所有声明添加到用户的Identity中(避免重复添加) var roleClaims = await _roleManager.GetClaimsAsync(role); foreach (var claim in roleClaims) { if (!identity.HasClaim(claim.Type, claim.Value)) { identity.AddClaim(claim); } } } return identity; } }
注册自定义工厂
在Program.cs的服务配置中,替换默认的IUserClaimsPrincipalFactory:
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, CustomUserClaimsPrincipalFactory<ApplicationUser, IdentityRole>>();
3. 确保JWT包含所有声明
生成JWT令牌时,要将用户的所有声明(包括从角色继承的)都加入到令牌的Claims集合中:
var user = await _userManager.FindByEmailAsync(email); if (user == null) /* 处理用户不存在的情况 */; // 收集用户的所有声明(包括自身+角色继承的) var claims = await _userManager.GetClaimsAsync(user); // 补充JWT标准声明(可选) claims.AddRange(new[] { new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()) }); // 生成JWT令牌 var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"])); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: _configuration["Jwt:Issuer"], audience: _configuration["Jwt:Audience"], claims: claims, expires: DateTime.UtcNow.AddHours(1), signingCredentials: creds); return new JwtSecurityTokenHandler().WriteToken(token);
验证效果
完成以上配置后:
- 直接在
AspNetUserClaims中添加AppPermissions.Root_Users_Create声明的用户,可以通过授权; - 被分配到包含
AppPermissions.Root_Users_Create声明的角色的用户,其ClaimsPrincipal会自动包含该声明,同样可以通过授权。
内容的提问来源于stack exchange,提问作者Alien
相关产品推荐
相关产品推荐

