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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 06:07:16