ASP.NET MVC中的基于角色访问控制(RBAC)与基于声明访问控制(CBAC)的区别

211

CBAC相对于RBAC有哪些主要优势?什么情况下使用CBAC更好,什么情况下使用RBAC更好?

我正在尝试理解CBAC模型的基本概念,但是整体思路还不够清晰。


1
这些概念对我来说仍然非常模糊。它们似乎也在做同样的事情。其中一个只是带有值的角色? - Zapnologica
12个回答

357

我将尝试以通俗易懂的方式解释基于角色/声明/权限的访问控制概念。这里我将呈现的代码片段是伪代码,可能无法编译。

什么是角色?

可以将角色看作职称,例如“销售经理”、“市场营销经理”、“管理员”等。

什么是声明?

声明可以比角色更广泛。你可以将声明视为标签。例如,你可以将一个人标记为“友好”、“健谈”、“欧洲人”、“摄影师”、“18岁成年人”等。从技术上讲,角色也可以被视为声明。

基于角色的访问控制

非常简单。不如我们通过一些例子来展示。 例如,你可以通过检查角色来允许访问网站上的某些页面,像这样:

    [Authorize(Roles="Sales Manager")]
    public ActionResult CreateCustomer()
    {
        return View();
    }

    [Authorize(Roles="Marketing Manager")]
    public ActionResult EditLandingPage()
    {
        return View();
    }

基于声明的访问控制

简单来说,在基于声明的访问控制中,当确定对页面的访问权限时,您检查声明而不是角色。

(这是伪代码。ClaimsAuthorize不是MVC中的内置类,而是您可以找到一些NuGet包或编写自己的类)

    [ClaimsAuthorize(Claims="Senior-Employee, Award-Winner-Employee, Experienced-On-Sales")]
    public ActionResult CreateCustomer()
    {
        return View();
    }


    [ClaimsAuthorize(Claims="Trust-worthy-Employee, President")]
    public ActionResult DeleteCustomer()
    {
        return View();
    }

    [ClaimsAuthorize(Claims="Adult-over-18years")]
    public ActionResult ViewImagesOfViolence()
    {
        return View();
    }

注意,我们允许用户基于他们声称的身份来访问页面,而不是检查角色。

RBAC vs CBAC

如果你问什么是基于角色的访问控制或基于声明的访问控制的好处,那么想一想这个页面“ViewImagesOfViolence”。当确定是否应该允许用户访问该页面时,检查声称的“成年人-18岁以上”是否更加直观?使用声明,您可以创建比角色更多的用户细分。在抽象意义上,所有角色也可以是声明,但不能将声明视为角色。

基于权限的访问控制

在允许访问页面时,不应该检查角色或声明,而应该考虑基于权限的访问控制。让我给你展示一些痛点。

当使用基于角色的身份验证时,如果您有一个用于创建客户的操作,并且希望具有“销售”角色的人能够执行该操作,那么您会编写以下代码:

[Authorize(Roles="Sale")]
public ActionResult CreateCustomer()
{
    return View();
}

后来,你意识到有时候“营销”角色的人应该能够创建客户。然后,你更新了你的Action方法如下。

[Authorize(Roles = "Sale", "Marketing")]
public ActionResult CreateCustomer()
{
    return View();
}

现在,你意识到一些市场营销人员可能无法创建客户,但是不能为那些处于市场营销部门的人分配不同的角色。因此,你被迫允许所有市场营销人员创建客户。

你发现另一个问题,每当你决定允许市场营销人员创建客户时,你必须更新所有MVC Action方法的Authorize属性,编译应用程序,进行测试和部署。几天后,你决定不是市场营销人员而是其他角色应该被允许执行该任务,于是你在代码库中搜索并删除所有的“市场营销”,然后将新角色名称添加到Authorize属性中...这不是一个健康的解决方案。在那一点上,你会意识到需要基于权限的访问控制。

基于权限的访问控制是一种将各种权限分配给各种用户、角色或声明,并在运行时从代码中检查用户是否有权限执行操作的方法。如果您将权限分配给角色或声明,那么您将检查已登录用户的角色或声明是什么。然后,您将检查这些角色或声明的可用权限。

您可以像这样定义一些权限集:

"CanCreateCustomer",“CanDeleteCustomer”、“CanEditCustomer”等等。

现在,您可以像这样装饰您的操作方法:

[Authorize(Permission="CanCreateCustomer")]
public ActionResult CreateCustomer()
{
    return View();
}
请注意,[Authorize(Permission="CanCreateCustomer")]可能并没有内置于MVC类库中,我只是以抽象的方式举例展示。可能会有一个NuGet包,其中Authorize类具有Permission属性。
现在,你可以看到,CreateCustomer操作方法总是需要具备“CanCreateCustomer”权限,并且它几乎不会改变。
谁将获得这些权限?
您可以直接向用户分配一组权限,但不要这样做。这将非常难以管理。相反,
您可以将一组权限分配给角色,或者将一组权限分配给声明(推荐)。
就像我提到的那样,角色也可以被视为声明。因此,您可以将角色视为声明。然后,在您的数据库中创建一个声明表。然后,创建另一个表来保存关系,其中每个声明可以包含多个权限。
这种安全模型为您提供了干净的代码实践。此外,当您编写您的操作方法时,您不必考虑谁可以使用此方法,而是您始终可以确保使用此方法的人将由管理员授予适当的权限。然后,管理员可以决定谁能做什么。而不是您作为开发人员。这就是您的业务逻辑与安全逻辑分离的方式。
每当有人登录时,您的应用程序将检查该用户可用的所有权限,并且该权限集将作为当前登录用户的附加属性可用,因此您不必一直从数据库中检查权限集。底线是,如果应用基于权限的访问控制,您将在应用程序中获得更多的安全逻辑控制。
如果您的应用程序非常小,只有两个角色:客户和管理员,并且没有机会让客户做除应用程序意图之外的任何事情,那么简单的基于角色的访问控制可能就足够了,但随着应用程序的增长,您将在某些时候开始感觉到需要基于权限的访问控制。

16
我不明白的是,角色在声明中起到了什么作用 - 在基于声明的系统中,角色不就是一组声明的集合,以便您可以批量分配吗?例如,您可以说Bob在营销部门担任角色,所有在营销部门的人都有“可以创建客户”和“可以查看广告系列”的声明。 - George Mauer
16
哇,我一直在尝试弄清楚这整个索赔的工作方式,但我从来没有理解所有那些模糊抽象的解释和例子。你的帖子是第一个让我豁然开朗、明白了信息的。非常感谢你如此简洁地解释。 - Leon Cullens
4
这是一个非常周到的解释,但我认为你在评论中提到“区别在于概念而不是技术”时已经意识到它是不完整的。实际上,在技术方面确实存在差异,这个答案没有涉及到。简而言之,我不同意它取决于您如何定义角色,因为这两种技术满足非常不同的要求。虽然该答案非常有帮助,可以展示应用太广泛的授权角色(或声明)所涉及的陷阱,但我还是犹豫是否进行纠正。不幸的是,这不是被问到的问题。 - hemp
11
假设我想要这样做: 1)“权限”是执行一个简单操作的权利,例如“创建客户”。权限名称以“can”开头 - CanCreateCustomer。权限名称在应用程序源代码中进行硬编码。 2)可以为用户分配一组权限,但不能直接分配。用户只能通过角色获得权限。 3)角色是一组权限,仅此而已。某些最终用户(管理员)可以动态创建具有任意权限集(从固定列表中选择)的新自定义角色。问题是:使用基于声明的模型,我可以像这样做吗? - Dmitry Arestov
10
微软文档指出:声明是表示主体是什么,而不是主体可以做什么的名称值对。 - CodingSoft
显示剩余7条评论

131
我已经多次实现了安全模型,并且也需要理解这些概念。在多次实践后,我对这些概念的理解如下。 角色是什么 角色=用户和权限的集合
一方面,角色是权限的集合。我喜欢称其为权限配置文件。在定义角色时,您基本上将一堆权限添加到该角色中,因此从这个意义上说,角色是权限配置文件。
另一方面,角色还是用户的集合。如果我将Bob和Alice添加到“管理者”角色中,则“管理者”现在包含了两个用户的集合,有点像一个组。
事实上,角色既是用户的集合,也是权限的集合。从视觉上来看,这可以被视为一个Venn图。 什么是组 组=用户的集合
“组”严格来说是用户的集合。组和角色的区别在于,角色还有权限的集合,而组只有用户的集合。 什么是权限 权限=主体可以做的事情 什么是权限集 权限集=权限的集合
在强大的RBAC系统中,权限也可以像用户一样分组。虽然组只是用户的集合,但权限集只是权限的集合。这使管理员可以一次向角色添加整个权限集合。 用户、组、角色和权限如何结合在一起 在强大的RBAC系统中,可以单独将用户添加到角色中以创建角色中的用户集合,或者可以将组添加到角色中以一次性向角色添加用户集合。无论哪种方式,角色都是通过单独添加或将组添加到角色或将用户和组合添加到角色来获取其用户集合的。权限也可以类比此方式。
权限可以单独添加到角色中以创建角色内的权限集合,或者可以将权限集添加到角色中。最后,可以将权限和权限集的混合添加到角色中。无论哪种方式,角色都是通过单独添加或将权限集添加到角色来获取其权限集合的。
角色的整个目的是将用户与权限联系起来。因此,角色是用户和权限的UNION。
声明是什么?
声明=主题“是”什么
声明不是权限。如先前的回答所指出的那样,声明是主题“是”而不是主题“能做”的内容。
声明不会替换角色或权限,它们是可用于进行授权决策的附加信息。
何时使用声明?
当需要进行授权决策但无法将用户添加到角色中或决策不基于用户与权限的关联时,我发现声明很有用。例如Facebook用户就是这种情况。Facebook用户可能不是添加到“角色”中的人,他们只是通过Facebook进行身份验证的访问者。虽然它不太适用于RBAC,但是这是一个授权决策的信息片段。
@CodingSoft在之前的回答中使用了夜总会比喻,我想进一步扩展。 在那个回答中,驾驶执照被用作一个包含一组声明的示例,其中出生日期代表其中一个声明,出生日期声明的值用于针对授权规则进行测试。 发放驾驶执照的政府是授予声明真实性的权威机构。 因此,在夜总会场景中,门口的保安查看人的驾驶执照,确保它是由可信任的权威机构发行的,方法是检查它是否为假ID(即必须是有效的政府发行ID),然后查看出生日期(驾驶执照上的许多声明之一),然后使用该值确定该人是否已经达到进入夜总会的年龄。 如果是这样,该人通过具有有效声明而不是身份扮演来通过授权规则。
现在,有了这个基础,我想进一步扩展。 假设夜总会所在的建筑物包括办公室,房间,厨房,其他楼层,电梯,地下室等,只有俱乐部员工才能进入。 此外,某些员工可能可以访问其他员工无法访问的某些地方。 例如,经理可能可以访问其他员工无法访问的上面的办公楼层。 在这种情况下,有两个角色。 经理和员工。
虽然访客对公共夜总会区域的访问是通过如上所述的单个声明进行授权的,但员工需要通过角色访问其他非公开限制房间。 对于他们来说,驾驶执照还不够。 他们需要一张员工徽章,可以扫描以进入门。 某个地方有一个RBAC系统,授予经理角色的徽章访问顶层,而授予员工角色的徽章访问其他房间。
如果由于某种原因需要按角色添加/删除特定房间,可以使用RBAC进行操作,但这不适合Claim。
软件中的权限
将角色编码到应用程序中是一个坏主意。这会将角色的目的硬编码到应用程序中。应用程序应该只有像特性标志一样的权限。特性标志通过配置可访问,权限通过用户安全上下文可访问,该上下文是从用户被放置在的所有角色收集的DISTINCT集合派生出来的。这就是我所说的“有效权限”。应用程序应该只呈现可能权限的菜单以供特性/操作使用。RBAC系统应该通过角色将这些权限与用户匹配。这样,就没有角色的硬编码,只有在删除或添加新的权限时才会更改权限。一旦将权限添加到软件中,就不应再更改它。只有在必要时(即在新版本中停止某个功能时)才应将其删除,只能添加新的权限。
最后一点。
授权与拒绝
强大的RBAC系统甚至CBAC系统应区分授予和拒绝。
向角色添加权限应该伴随着GRANT或DENY。当检查权限时,所有GRANTed权限都应添加到用户的有效权限列表中。然后,在完成所有这些操作之后,拒绝权限的列表应导致系统从有效权限列表中删除这些权限。
这允许管理员“微调”主体的最终权限。最好能够直接向用户添加权限。这样,您可以将用户添加到经理角色,并获得对所有内容的访问权限,但是也许您想要拒绝男性用户进入女厕所。因此,您将男性用户添加到经理角色中,并向用户对象添加一个具有DENY的权限,以便仅取消该女士房间的访问权限。
实际上,这将是声明的一个好选择。如果用户有一个声明“gender=male”,那么在经理角色中具有对所有房间的访问权限,但女士洗手间还需要声明gender=female,而男士洗手间需要声明gender=male。通过这种方式,不必为男性用户配置DENY权限,因为声明强制执行会为每个人提供单个授权规则来处理这一点。但是,两种方法都可以实现。
重点是使用DENIAL权限使角色管理变得更容易,因为可以实现例外情况。
下面是我很久以前制作的显示RBAC模型的图表。我没有声明的图形,但您可以想象它们只是附加到用户身上的属性。此外,图表不显示组(我需要随时更新它)。 这是上述RBAC的图表 2019年4月7日更新
根据@Brent的反馈(谢谢)...删除了先前答案的不必要引用,并解释了@CodingSoft提供的“夜总会”隐喻的原始基础,以便使此答案可理解而无需阅读其他答案。

7
这是一份很好的解释,应该得到顶部的赞同,感谢您添加了例子和图表。 - Optimae
2
很棒的答案。一个建议是删除对其他答案的引用。每个答案都应该独立存在,尽管我读了其他答案,但并不是每个人都会这么做。 - Brent
1
谢谢你,Brent。绝妙的想法。我仔细阅读了答案,并尝试通过删除对其他答案的不必要引用以及解释夜总会隐喻的基础,使其不需要阅读其他答案来进行改进。如果您有任何进一步的改进建议,我将很快应用它们。再次感谢。 - Ricardo
2
这太棒了,而且用通俗易懂的语言解释得非常好 - 谢谢您。 - toy
1
你好@SPopenko,感谢你的赞美之词。1)像OAuth这样的身份验证提供程序是用于“身份验证”(即证明你是谁),而此线程是关于“授权”(即你被允许做什么)。一旦身份验证得到证明,就会应用授权。2)这些权限针对的是数据而不是软件工件。此线程是关于授权功能,如按钮、访问页面、选项卡等。用户创建的数据是另一回事,超出了此线程的范围。我建议为此提出一个新问题。3)很抱歉我没有PoC。 - Ricardo
显示剩余2条评论

56

我不完全同意Emran的回答。

[Authorize(Roles="Sale")]

太天真了

问题在于如何解决

  [Authorize(Roles="CustomerCreator")]

与...不同

 [ClaimAuthorize(Permission="CanCreateCustomer")]
如果两者一样好,我们为什么需要声明呢?
我认为是因为
声明的概念比角色更加通用
针对上面的例子,我们可以说"CustomerCreator"是由"Asp.NETroleProvider"提供的"role"类型的声明。
其他声明的示例:
1. "AAA"是由"MYExamSite.com"提供的"MYExamSite.Score"类型的声明。 2. "Gold"是由"MYGYMApp"提供的"MYGYM.Membershiptype"类型的声明。

11
我认为这个答案有价值,因为它强调了声明和其等效角色之间的根本区别,而不是描述一个可以使用声明或基于角色的授权模型有效实现的场景。+1 - Katstevens
这就是在MSSQL中如何处理角色。它有DBDataReader和DBDataWriter,而不是MyAppDB和HisAppDB。 - Behrooz
在RBAC中,角色是分配权限的,那么角色如何意味着任命呢? - Wouter
感谢您的反馈。我已经更新了我的回答,希望这一次我已经纠正了之前提到的含糊不清和令人困惑的部分。 - Emran Hussain

50
接受的答案似乎将角色描绘为一种钝器,而声明则是一种灵活的工具,但在其他方面它们似乎几乎相同。不幸的是,这种定位对声明的概念不利,可能根本反映了对其目的的轻微误解。
角色只存在于隐含的范围内并且只有在应用程序或组织范围内才有意义(例如Role=Administrator)。另一方面,任何人都可以“发出”声明。例如,Google身份验证可能会生成包括用户“电子邮件”的声明,从而将该电子邮件附加到一个身份上。Google作出声明,应用程序选择是否理解和接受该声明。 应用程序本身随后可以附加名为“authenticationmethod”的声明(如ASP.NET MVC Core Identity所做)并赋值为“Google”。每个声明都包含一个范围,以便能够确定声明在外部、本地或两者(或更细粒度根据需要)中有意义。
关键点是所有声明都明确附加到一个身份上,并包含明确的范围。当然,这些声明可以用于授权,而ASP.NET MVC通过Authorize属性提供了对此的支持,但这并不是声明的唯一或必要目的。它肯定不能将其与角色区分开来,因为本地范围的授权可以完全使用角色实现。
因此,人们可以选择使用角色、声明或两者用于授权之目的,并且可能不会对任何一种方法产生天然的优劣之分,只要这些角色和声明是局部范围的即可。但是,如果授权取决于外部身份声明,则角色将不足以胜任。您必须接受外部声明并将其转换为本地作用域的角色。这没有什么不好,但它引入了一个间接层并且丢失了上下文。

4
好的回答...我认为我理解了你的意思,但如果你能提供一些具体的例子(可能是在ASP MVC上下文中),那就更清晰了...这个答案太抽象了,我很难掌握它。 - Rosdi Kasim
2
第二段确实包括与ASP.NET MVC Core Identity和Google身份验证相关的具体示例。听起来更详细的Core新模型演示会有所帮助 - 对此,我建议阅读这篇文章:https://andrewlock.net/introduction-to-authentication-with-asp-net-core/ - hemp
在我看来,最佳答案 - mrmashal

10

RBAC和CBAC之间的基本区别在于:

RBAC:用户必须被分配到一个角色才能被授权执行某个操作。

CBAC:用户必须具有正确值的声明,以便按照应用程序的期望进行授权。基于声明的访问控制编写起来更加优雅,更易于维护。

此外,声明是由一个受信任的授权服务(安全服务令牌STS)向应用程序发放的,该应用程序是信任该服务的(依赖方)。


9

在决定哪种方法最好之前,首先需要分析需要身份验证的内容是什么。来自基于声明的授权

一个声明不代表主题能够做什么。例如,您可能拥有由当地驾照管理机构颁发的驾驶执照。您的驾驶执照上有您的出生日期。在这种情况下,声明名称将是DateOfBirth,声明值将是您的出生日期,例如1970年6月8日,发行人将是驾驶执照管理机构。基于声明的授权最简单的方式是检查声明的值并根据该值允许访问资源。例如,如果您想要进入一个夜总会,授权过程可能如下:

门卫将评估您的出生日期声明的价值以及他们是否信任发行人(驾驶执照管理机构),然后才会允许您进入。

从这个例子中,我们可以看到通过基于声明的授权访问夜总会与工作在夜总会的员工所需的授权类型有很大的不同,在这种情况下,夜总会的工作人员将需要基于角色的授权,而夜总会的访客则不需要。因为夜总会的访客在夜总会有共同的目的,因此在这种情况下,基于声明的授权适合夜总会的访客。

基于角色的授权

当创建身份时,它可能属于一个或多个角色。例如,Tracy 可能属于管理员和用户角色,而 Scott 只属于用户角色。如何创建和管理这些角色取决于授权过程的后备存储。角色通过 ClaimsPrincipal 类上的 IsInRole 方法向开发人员公开。


8
更广泛地说,您应该考虑基于属性的访问控制(ABAC)。RBAC和ABAC都是由NIST(国家标准与技术研究所)定义的概念。另一方面,CBAC是微软推动的一种模型,非常类似于ABAC。
在此阅读更多:
- 基于角色的访问控制NIST页面 - 基于属性的访问控制NIST页面

3
虽然建议使用基于属性的访问控制很好,但提供关于在MVC/WebAPI堆栈中实施这些最佳实践的链接会更好。谢谢。 - Itanex

8

如果您需要一个现实生活中的例子;

你有一个学校系统,老师可以登录并查看他们的学生。这些老师属于“教师”角色。但我们不希望所有老师都能看到所有学生,因此我们需要通过他们的身份来区分同一级别的人。

  1. Mary-数学教师(身份:数学)->只能看到数学学生
  2. John-物理教师(身份:物理)->只能看到物理学生
  3. Adam-物理和化学教师(身份:物理,化学)->可以看到物理和化学学生。

虽然这三位老师都属于教师角色,但他们只能看到与其对应的身份的学生。

还有一个名叫Mike的校长:

  1. Mike-校长(角色:管理员,身份:无)->由于他属于管理员角色,可以看到和管理所有学生,即使他没有任何身份。

如果我们需要区分管理员级别的人,我们可以为每个管理员分配相关的身份。


7

角色仅是声明的一种类型。同样地,还有许多其他声明类型,例如用户名就是其中之一。


3

你还可以以一种声明方式来管理角色。

不要创建反映业务角色的授权角色,而是创建反映操作角色的角色,例如CreateCustomer、EditCustomer、DeleteCustomer。根据需要注释方法。

将个人映射到一组操作角色并不是一件简单的事情,特别是当角色列表变得越来越大时。因此,您需要在更低级别的粒度(例如销售、营销)上管理业务角色,并将业务角色映射到所需的操作角色。也就是说,将用户添加到业务角色中,它会将它们映射到现有授权表中所需的(操作)角色。

您甚至可以覆盖业务角色,直接将人员添加到操作角色中。

由于建立在已经起作用的基础上,您不会撤消现有的授权过程。只需要几个额外的表来实现这种方法即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接