ASP.NET 身份中角色与声明的最佳实践

Best Practices for Roles vs. Claims in ASP.NET Identity

我对在 ASP.NETIdentity 中使用 claims 完全陌生,想了解使用 Roles and/or Claims 的最佳实践。

看完这些之后,我仍然有一些问题,例如...

问:我们不再使用角色了吗?
问:如果是这样,为什么仍然提供角色?
问:我们应该只使用 Claims 吗?
问:我们应该同时使用角色和声明吗?

我最初的想法是我们 "should" 一起使用它们。我将 Claims 视为他们支持的 Roles 的子类别。

例如:
角色:会计
声明:CanUpdateLedger、CanOnlyReadLedger、CanDeleteFromLedger

问:它们是相互排斥的吗?
问:还是只去索赔更好,"fully-qualify" 你索赔?
问:那么这里的最佳实践是什么?

示例:同时使用角色和声明
当然,您必须为此编写自己的属性逻辑...

[Authorize(Roles="Accounting")]
[ClaimAuthorize(Permission="CanUpdateLedger")]
public ActionResult CreateAsset(Asset entity)
{
    // Do stuff here

    return View();
}

示例:完全限定您的声明

[ClaimAuthorize(Permission="Accounting.Ledger.CanUpdate")]
public ActionResult CreateAsset(Asset entity)
{
    // Do stuff here

    return View();
}

角色是一个符号类别,它将共享相同级别安全权限的用户集合在一起。基于角色的授权需要首先识别用户,然后确定用户被分配到的角色,最后将这些角色与被授权访问资源的角色进行比较。

相比之下,声明不是基于组的,而是基于身份的。

来自 Microsoft documentation:

When an identity is created it may be assigned one or more claims issued by a trusted party. A claim is a name value pair that represents what the subject is, not what the subject can do.

安全检查稍后可以根据一项或多项声明的价值确定访问资源的权利。

可以同时使用这两种类型,或者在某些情况下使用一种类型而在其他情况下使用另一种类型。这主要取决于与其他系统的互操作以及您的管理策略。例如,对于经理来说,管理分配给某个角色的用户列表可能比管理分配了特定声明的用户更容易。声明在 RESTful 场景中非常有用,您可以将声明分配给客户端,然后客户端可以提交声明以获得授权,而不是为每个请求传递用户名和密码。

正如@Claies 完美解释的那样,声明可能更具描述性,并且是一种深层角色。我将它们视为您的角色 ID。我有一个健身房 id,所以我属于会员角色。我也在参加跆拳道课程,所以我为他们申请了一个跆拳道 ID。我的申请需要声明新角色以符合我的会员权利。相反,我有我所属的每个组 class 的 ID,而不是许多新的成员类型。这就是为什么 claims 更适合我。

Barry Dorrans 有一个很棒的解释视频,讲述了使用声明而不是角色的优势。他还指出,为了向后兼容,角色仍在 .NET 中。该视频非常详细地介绍了声明、角色、策略、授权和身份验证的工作方式。

您可以在这里找到它:ASP.NET Core Authorization with Barr Dorrans

几十年来使用了各种身份验证和授权技术,我当前的 MVC 应用程序使用以下方法。

声明用于所有授权。用户被分配了一个角色(多个角色是可能的,但我不需要这个)- 下面有更多内容。

按照惯例,使用了 ClaimsAuthorize 属性 class。由于大多数控制器操作都是 CRUD,我在代码优先数据库生成中有一个例程,它迭代所有控制器操作并为 Read/Edit/Create/Delete 的每个控制器操作属性创建声明类型。例如。来自,

[ClaimsAuthorize("SomeController", "Edit")]
[HttpPost]

为了在 MVC 视图中使用,基本控制器 class 呈现视图包项

        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            // get user claims
            var user = filterContext.HttpContext.User as System.Security.Claims.ClaimsPrincipal;

            if (user != null)
            {
                // Get all user claims on this controller. In this controler base class, [this] still gets the descendant instance type, hence name
                List<Claim> claims = user.Claims.Where(c => c.Type == this.GetType().Name).ToList();

                // set Viewbag with default authorisations on this controller
                ViewBag.ClaimRead = claims.Any(c => c.Value == "Read");
                ViewBag.ClaimEdit = claims.Any(c => c.Value == "Edit");
                ViewBag.ClaimCreate = claims.Any(c => c.Value == "Create");
                ViewBag.ClaimDelete = claims.Any(c => c.Value == "Delete");
            }

            base.OnActionExecuting(filterContext);
        }

对于网站菜单和其他非控制者行为,我有其他主张。例如。用户是否可以查看特定货币字段。

bool UserHasSpecificClaim(string claimType, string claimValue)
{
    // get user claims
    var user = this.HttpContext.User as System.Security.Claims.ClaimsPrincipal;

    if (user != null)
    {
        // Get the specific claim if any
        return user.Claims.Any(c => c.Type == claimType && c.Value == claimValue);
    }

    return false;
}

public bool UserHasTradePricesReadClaim
{
    get
    {
        return UserHasSpecificClaim("TradePrices", "Read");
    }
}

那么角色适合什么地方?

我有一个 table 将角色链接到一组(默认)声明。当设置用户授权时,默认是给用户他们角色的声明。每个用户可以拥有比默认更多或更少的声明。为了简化编辑,声明列表按控制器和操作(连续)显示,然后列出其他声明。按钮与一些 Javascript 到 select 的一组操作一起使用,以最大限度地减少 select 声明所需的 "clicking"。在保存时,用户声明将被删除并添加所有 selected 声明。 Web 应用程序仅加载声明一次,因此任何更改都必须提示重新加载此静态数据。

因此,管理人员可以 select 每个角色中有哪些声明以及用户在将它们设置为角色后拥有哪些声明以及那些默认声明。该系统只有少量用户,因此管理这些数据非常简单

要了解 Roles 和 Claims 之间的区别,您必须面对角色的局限性并感受 claims 是如何解决这些问题的,所以让我给您 2 个场景来认识 claims 在角色无法解决这些问题时的力量:

1- 您的网站有两个模块(页面、服务等),第一个模块针对儿童(18 岁以下),另一个模块针对成人(18 岁以上) 您的用户身份有生日声明

您需要针对此声明创建策略,以便根据此值授予每个模块的授权,如果用户的年龄超过 18 岁,则他可以进入成人模块,而不是在此年龄之前.

角色是布尔数据类型,您可以拥有或不拥有角色,它没有多值。

2- 您的站点具有用户角色,您希望在不更改代码的情况下阻止用户访问以进行一些维护。

在声明中,您可以创建一个 UnderConstrain 策略,如果该策略为真,则用户无法查看页面,从而为角色用户授予 属性 授权。

在撰写此答案时,我们正处于“.NET 5.0”阶段,“.NET 6.0”指日可待。这是我对所见所闻的理解:

Q: Do we no longer use Roles?

是的,您不应该再使用角色(至少不是您在以前的框架中使用的方式。

Q: If so, why are Roles still offered?

做升级项目easier/faster?

Q: Should we only use Claims?

是的。但请务必查看@Jonathan Ramos 在此处发布的答案中的视频。

Q: Should we use Roles & Claims together?

不,但您当然可以将角色放入声明中,但请务必将您的项目升级为仅使用声明。

而且您不必编写自己的属性,您应该为此使用 policy,因为这是较新框架的方式。如果您需要自己的属性,那么您就“做错了”,只需创建自己的 Requirement(handler) 即可,这就是整个 'new' 政策的全部内容。 在当前框架中,属性 ClaimAuthorize 甚至不再可用。