简体   繁体   English

澄清身份授权:将声明用作角色,角色和声明或角色声明

[英]Clarifying Identity Authorization: using Claims as Roles, Roles and Claims or Role Claims

I'm starting with ASP.NET Identity's Claim authorization and I would like to clarify the way of proceeding with them if I need the "roles" concept in my app. 我从ASP.NET身份的声明授权开始,如果我需要在我的应用程序中使用“角色”概念,我想澄清继续使用它们的方式。

Note: I'm really new with this, so all the concepts are flying in my head, please be kind, and further clarifications/Corrections about any concept will be much appreciated. 注意:我对此真的很新,所以所有的概念都在脑子里浮现,请善待,任何概念的进一步澄清/更正将非常感激。

1.- Suppose, I need the "roles" concept for Admin and User roles, so my first though was to add claims to ApplicationUser s like: 1.-假设,我需要管理员和用户角色的“角色”概念,所以我的第一个想法是向ApplicationUser添加声明,如:

user.Claims.Add(new IdentityUserClaim<string> { ClaimType = "Role", ClaimValue = "Admin" });

*Where "user" is an ApplicationUser . *“user”是ApplicationUser

But then I read that it is already done by the framework as it has some predefined claim types, so the code above could be: 但后来我读到它已经由框架完成,因为它有一些预定义的声明类型,所以上面的代码可能是:

user.Claims.Add(new IdentityUserClaim<string> { ClaimType = ClaimTypes.Role, ClaimValue = "Admin" });

Is that approach correct? 这种做法是否正确? Or should i use the "old" role concept and add a role to the user like: 或者我应该使用“旧”角色概念并向用户添加角色,如:

await _roleManager.CreateAsync(new IdentityRole("Admin"));    
await _userManager.AddToRoleAsync(user, "Admin");

2.- Now suppose that I have roles defined as claims, how could I check the authotization of them? 2.-现在假设我将角色定义为声明,我如何检查它们的自动化? I mean, will it work? 我的意思是,它会起作用吗?

[Authorize(Roles = "Admin")]

Or should I include a Policy statement to check the role claim? 或者我应该包含一个政策声明来检查角色声明?

/* In startup ConfigureServices method*/
options.AddPolicy("IsAdmin", policy => {
                policy.RequireClaim(ClaimTypes.Role, "Admin");
                });

...

/*In a controller class*/
[Authorize(Policy = "IsAdmin")]
<controller here>

3.- And now, what is the correct way of storing my custom claims? 3.-现在,存储我的自定义索赔的正确方法是什么? I mean, ASP.NET's ClaimTypes class is just a bunch of const string values and all the sample codes about Claims stores them in similar classes like: 我的意思是,ASP.NET的ClaimTypes类只是一堆const string值,所有关于Claims的示例代码都将它们存储在类似的类中,如:

public static class ClaimData
{
    public static List<string> AdminClaims { get; set; } = new List<string>
                                                        {
                                                            "Add User",
                                                            "Edit User",
                                                            "Delete User"
                                                        };
}

Is that ok? 这可以吗?

Final note.- I've also see at the internet the concept of "Role Claim", which is explained in this blog post: http://benfoster.io/blog/asp-net-identity-role-claims 最后的注意事项.-我还在互联网上看到了“角色声明”的概念,该博客文章对此进行了解释: http//benfoster.io/blog/asp-net-identity-role-claims

What is that? 那是什么? If I wasn't confused enough, now there is a third way of Authorizing users. 如果我不够困惑,现在有第三种方式授权用户。 Is it the better way to use roles as claims? 这是使用角色作为声明的更好方法吗?

The approach you describe seems correct. 您描述的方法似乎是正确的。 Everything depends upon your requirements. 一切都取决于您的要求。

Imagine you have several features in your application, if you choose to use roles, the code belonging to the feature must check everytime if the user is in a particular set of roles to use the feature. 假设您的应用程序中有多个功能,如果您选择使用角色,则每次如果用户处于特定角色集中以使用该功能,则必须检查属于该功能的代码。 This approach becomes quite unmanageable when the features and roles grow, because you must take into account the combination of roles into every single feature. 当功能和角色增长时,这种方法变得非常难以管理,因为您必须将角色组合考虑到每个功能中。 In this example, a user can perform the management operation X only if it is PowerUser or Administrator . 在此示例中,用户只有在PowerUserAdministrator才能执行管理操作X Now, this seems easy and strightforward, but, what happens if you add a new role, ALittleBitMorePowerful , which is a User who can also perform the X operation. 现在,这似乎很容易和strightforward,但是,如果你添加一个新的角色,会发生什么ALittleBitMorePowerful ,这是一个User谁也可以执行X操作。 To achieve this result you have to review everything and change the checks (this implies retesting the whole thing). 要实现此结果,您必须检查所有内容并更改检查(这意味着重新测试整个事情)。

If you designed the feature X with a claim CanPerformX , your introduce a layer of abstraction: your code will not care about the role of the user, but will check only for its own claim. 如果您使用声明CanPerformX设计了功能X ,那么您需要引入一个抽象层:您的代码不会关心用户的角色,而只会检查自己的声明。 If you ever rework how the claims are associated to the users, your effective code will not change (which in the end means no formal regressions have been introduced). 如果您修改了声明与用户的关联方式,那么您的有效代码将不会更改(这最终意味着没有引入正式的回归)。

Roles are designed to be broad while Claims have been designed to be fine grain. 角色设计宽广,而声称设计为细粒度。 However, as you read in the link, you may think a role as "big claim", or a claim as a "small role". 但是,当您在链接中阅读时,您可能会将角色视为“重要声明”,或将声明视为“小角色”。

I post a small excerpt of a code of mine which supports custom roles but fixed claims. 我发布了一小段我的代码摘录,它支持自定义角色但固定声明。 Define the claims 定义声明

    internal static class PolicyClaims
    {
        public const string AdministratorClaim = @"http://myorganization/2019/administrator";
        public const string Operation1Claim = @"http://myorganization/2019/op1";
        public const string Operation2Claim = @"http://myorganization/2019/op2";
        public const string ObtainedClaim = @"true";
    }

Define the policies 定义策略

    internal static class Policies
    {
        public const string RequireAdministrator = "RequireAdministrator";
        public const string RequireOp1 = "RequireOp1";
        public const string RequireOp2 = "RequireOp2";

        public const string AlwaysDeny = "AlwaysDeny";

        public static void ConfigurePolicies(IServiceCollection services)
        {
            services.AddAuthorization(options => options.AddPolicy(RequireAdministrator, policy => policy.RequireClaim(PolicyClaims.AdministratorClaim)));
            services.AddAuthorization(options => options.AddPolicy(RequireOp1, policy => policy.RequireClaim(PolicyClaims.Operation1Claim)));
            services.AddAuthorization(options => options.AddPolicy(RequireOp2, policy => policy.RequireClaim(PolicyClaims.Operation2Claim)));
            services.AddAuthorization(options => options.AddPolicy(AlwaysDeny, policy => policy.RequireUserName("THIS$USER\n\r\t\0cannot be created")));
        }
    }

Register the policies in Startup.RegisterServices Startup.RegisterServices注册策略

    Policies.ConfigurePolicies(services);

Where you authenticate the user, decide which claims you need to add based upon your logic (omitted some parts to focus on the concepts) 在对用户进行身份验证的位置,根据您的逻辑确定需要添加的声明(省略一些部分以关注概念)

    [AllowAnonymous]
    [Route("api/authentication/authenticate")]
    [HttpPost()]
    public async Task<IActionResult> Authenticate([FromBody] LoginModel model)
    {
        if (ModelState.IsValid)
        {
            var user = m_UserManager.Users.FirstOrDefault(x => x.UserName == model.UserName);

            if (user == null)
            {
                ...
            }
            else
            {
                var result = await m_SignInManager.CheckPasswordSignInAsync(user, model.Password, false);
                if (result.Succeeded)
                {
                    var handler = new JwtSecurityTokenHandler();
                    var tokenDescriptor = new SecurityTokenDescriptor
                    {
                        Subject = new ClaimsIdentity(new Claim[]
                        {
                        new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                        new Claim(ClaimTypes.Name, model.UserName)
                        }),
                        Expires = DateTime.UtcNow.AddHours(2),
                        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(InstanceSettings.JWTKey), SecurityAlgorithms.HmacSha256Signature)
                    };

                    var roles = await m_UserManager.GetRolesAsync(user);

                    AddClaims(tokenDescriptor, roles);

                    var token = handler.CreateToken(tokenDescriptor);
                    var tokenString = handler.WriteToken(token);

                    return ...
                }
                else
                {
                    ...
                }
            }
        }
        return ...
    }

    private static void AddClaims(SecurityTokenDescriptor tokenDescriptor, IList<string> roles)
    {
        if (roles.Any(x => string.Equals(Constants.AdministratorRoleName, x, StringComparison.OrdinalIgnoreCase)))
        {
            tokenDescriptor.Subject.AddClaim(new Claim(PolicyClaims.AdministratorClaim, PolicyClaims.ObtainedClaim));

            tokenDescriptor.Subject.AddClaim(new Claim(PolicyClaims.Operation1Claim, PolicyClaims.ObtainedClaim));
            tokenDescriptor.Subject.AddClaim(new Claim(PolicyClaims.Operation2Claim, PolicyClaims.ObtainedClaim));
        }
        ... query the database and add each claim with value PolicyClaims.ObtainedClaim ...
    }

Finally, you can use the policies to protect your code: 最后,您可以使用这些策略来保护您的代码:

    [Authorize(Policy = Policies.RequireAdministrator)]
    [HttpPost("execute")]
    public async Task<IActionResult> ExecuteOperation([FromBody] CommandModel model)
    {
        ...
    }

Note that in this approach, I hardcoded certain claims to the administrator because I'd like to prevent the administrator removing certain claims. 请注意,在此方法中,我将某些声明硬编码到管理员,因为我想阻止管理员删除某些声明。 However, this is not mandatory. 但是,这不是强制性的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM