188

使用CBACRBAC的主要好处是什么?何时使用 CBAC 更好,何时使用 RBAC 更好?

我正在尝试理解 CBAC 模型的一般概念,但总体思路对我来说仍然不清楚。

4

12 回答 12

318

我将尝试用外行的术语来解释基于角色/声明/权限的访问控制概念。我将在这里展示的代码片段是伪代码,可能会也可能不会编译。

什么是角色?

角色可以被认为是职位。像“销售经理”、“市场经理”、“管理员”等。

索赔是什么?

声明可以比角色更广泛。您可以将 Claim 视为 TAG。例如,您可以将一个人标记为“友好”、“健谈”、“欧洲人”、“摄影师”、“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 与 CBAC

好的,现在,如果您问基于角色的访问控制或基于声明的访问控制有什么好处,那么请考虑一下“ViewImagesOfViolence”这个页面。在确定是否应允许用户访问该页面时,检查“18 岁以上成人”声明不是更直观吗?总之,使用声明,您可以在用户中创建更多细分来比较角色。在抽象意义上,所有的角色也可以是声明,但声明不能被认为是角色。

基于权限的访问控制

在允许查看页面的权限时,与其检查角色或声明,不如考虑基于权限的访问控制。让我告诉你一些痛点。

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

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

后来,您意识到,有时,来自“营销”角色的人应该能够创建客户。然后,您像这样更新您的 Action 方法

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

现在,您意识到某些营销人员一定不能创建客户,但不可能为营销人员分配不同的角色。因此,您被迫允许所有营销人员创建客户。

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

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

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

“CanCreateCustomer”、“CanDeleteCustomer”、“CanEditCustomer”……等等。

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

[Authorize(Permission="CanCreateCustomer")]
public ActionResult CreateCustomer()
{
    return View();
}

请注意,[Authorize(Permission="CanCreateCustomer")] 可能没有内置到 MVC 类库中,我只是抽象意义上的示例。可以有一个 NuGet 包,该包将在 Authorize 类中具有 Permission 属性。

现在,您可以看到,CreateCustomer 操作方法将始终需要“CanCreateCustomer”权限,并且它永远不会改变或几乎不会改变。

谁将获得权限?

您可以将一组权限直接分配给用户。但不要那样做。管理它将非常困难。相当,

您可以将一组权限分配给角色,或者您可以将一组权限分配给声明(推荐)。

正如我所提到的,角色也可以被认为是声明。因此,您可以将角色视为声明。然后,您可以在数据库中创建一个声明表。然后,创建另一个表来保存每个声明可以包含多个权限的关系。

此安全模型为您提供干净的代码实践。此外,当您编写 Action Method 时,您不必考虑谁可以使用此方法,而是始终可以确保使用此方法的任何人都将获得 Admin 授予的适当权限。然后,管理员可以决定谁能够做什么。不是您作为开发人员。这就是您的业务逻辑与安全逻辑分离的方式。

每当有人登录时,您的应用程序将检查该用户可用的任何权限,并且该权限集将作为当前登录用户的附加属性提供,因此您不必一直从数据库中检查权限集. 底线是,如果您应用基于权限的访问控制,您可以更好地控制应用程序中的安全逻辑。

如果您的应用程序是一个非常小的应用程序,其中只有 2 个角色:客户和管理员,并且客户除了在您的应用程序中应该做的事情之外,没有机会做任何其他事情,那么也许,简单的角色基于权限的访问控制将达到目的,但是随着您的应用程序的增长,您将在某些时候开始感到需要基于权限的访问控制。

于 2014-04-02T18:23:53.857 回答
111

我现在已经实施了很多次安全模型,并且也不得不围绕这些概念来思考。做了很多次,这里是我对这些概念的理解。

什么是角色

角色 = 用户和权限的联合

一方面,角色是权限的集合。我喜欢称它为权限配置文件。在定义角色时,您基本上会在该角色中添加一堆权限,因此从这个意义上讲,角色就是权限配置文件。

另一方面,角色也是用户的集合。如果我将 Bob 和 Alice 添加到角色“经理”,那么“经理”现在包含两个用户的集合,有点像一个组。

事实上,角色既是用户的集合,也是权限的集合。从视觉上看,这可以看作是一个维恩图。

什么是组

组 = 用户集合

“组”严格来说是用户的集合。Group 和 Role 的区别在于 Role 也有 Permissions 的集合,而 Group 只有 User 的集合。

什么是权限

权限 = 主体可以做什么

什么是权限集

权限集 = 权限集合

在健壮的 RBAC 系统中,权限也可以像用户一样进行分组。组只是用户的集合,而权限集只是权限的集合。这允许管理员一次将整个权限集合添加到角色。

用户、组、角色和权限如何结合在一起

在一个健壮的 RBAC 系统中,可以将用户单独添加到角色中以创建角色中的用户集合,或者可以将组添加到角色中以一次将用户集合添加到角色中。无论哪种方式,角色都可以通过单独添加或通过将组添加到角色或通过将用户和组的混合添加到角色来获取其用户集合。可以以相同的方式考虑权限。

可以将权限单独添加到角色以在角色内创建权限集合,或者可以将权限集添加到角色。最后,可以将权限和权限集的混合添加到角色中。无论哪种方式,角色都可以通过单独添加或通过将权限集添加到角色来获取其权限集合。

角色的全部目的是将用户与权限结合起来。因此,角色是用户和权限的联合。

什么是索赔

声明 = 主题“是”什么

声明不是许可。正如前面的答案所指出的,声明是主题“是”而不是主题“可以做什么”。

声明不会取代角色或权限,它们是可用于做出授权决定的附加信息。

何时使用声明

我发现当用户无法添加到角色或决定不是基于用户与权限的关联时需要做出授权决定时,声明很有用。Facebook 用户的示例导致了这一点。Facebook 用户可能不是被添加到“角色”的人......他们只是通过 Facebook 进行身份验证的一些访问者。尽管它不完全适合 RBAC,但它是用于做出授权决定的一条信息。

@CodingSoft 在之前的答案中使用了夜总会隐喻,我想对此进行扩展。在该答案中,驾驶执照被用作包含一组声明的示例,其中出生日期代表其中一个声明,并且 DateOfBirth 声明的值用于针对授权规则进行测试。颁发驾驶执照的政府是赋予索赔真实性的权威。因此,在夜总会场景中,门口的保镖会查看该人的驾驶执照,通过检查它是否是假身份证(即必须是政府颁发的有效身份证)来确保它是由受信任的权威机构签发的,然后查看出生日期(驾驶执照上的众多声明之一),然后使用该值来确定此人是否足够大,可以进入俱乐部。如果是这样,

现在,考虑到这个基础,我现在想进一步扩展它。假设夜总会所在的建筑物包含只有俱乐部员工才能进入的办公室、房间、厨房、其他楼层、电梯、地下室等。此外,某些员工可能可以访问其他员工可能无法访问的某些地方。例如,经理可能有权访问其他员工无法访问的办公楼层。在这种情况下,有两个角色。经理和员工。

虽然如上所述,访客进入公共夜总会区域是由单一索赔授权的,但员工需要通过角色访问其他非公共受限房间。对他们来说,驾照是不够的。他们需要的是他们扫描进入门的员工徽章。某处有一个 RBAC 系统,该系统授予经理角色中的徽章访问顶层,以及员工角色中的徽章访问其他房间。

如果出于某种原因需要由角色添加/删除某些房间,这可以使用 RBAC 完成,但它不适合索赔。

软件权限

将角色编码到应用程序中是一个坏主意。这将角色的目的硬编码到应用程序中。应用程序应该拥有的只是类似于功能标志的权限。在功能标志可以通过配置访问的地方,权限可以通过用户安全上下文访问,该上下文是由从用户所在的所有角色收集的权限的 DISTINCT 集合派生的。这就是我所说的“有效权限”。应用程序应该只显示一个菜单功能/操作的可能权限。RBAC 系统应该通过角色将这些权限与用户结合起来。这样,没有对角色进行硬编码,并且权限更改的唯一时间是删除或添加新权限时。一旦将权限添加到软件中,就永远不要更改它。仅在必要时才应将其删除(即,当新版本中停止使用功能时)并且只能添加新功能。

最后一点。

格兰特与拒绝

一个强大的 RBAC 系统甚至 CBAC 系统都应该区分 Grants 和 Denials。

向角色添加权限应带有 GRANT 或 DENY。选中权限后,应将所有已授予的权限添加到有效权限的用户列表中。然后在完成所有这些之后,拒绝权限列表应该导致系统从有效权限列表中删除这些权限。

这允许管理员“调整”主题的最终权限。最好也可以直接将权限添加到用户。这样,您可以将用户添加到经理角色并且他们可以访问所有内容,但您可能希望拒绝访问女士洗手间,因为该用户是男性。因此,您将男性用户添加到经理角色,并使用 DENY 将权限添加到用户对象,这样它就只取消了该女士的房间访问权限。

实际上,这将是一个很好的索赔候选人。如果用户有一个声明“性别=男性”,那么担任经理角色可以访问所有房间,但女士洗手间也需要声明性别=女性,而男性洗手间需要声明性别=男性。这样一来,就不必为男性用户配置 DENY 权限,因为声明执行会为每个人使用单一授权规则来解决这个问题。但是,它可以通过任何一种方式完成。

关键是,使用 DENIAL of Permissions 可以更轻松地管理角色,因为可以实现异常。

下面是我很久以前制作的图表,显示了 RBAC 模型。我没有声明的图形,但您可以想象它们只是附加到用户的属性,无论它们在哪里。此外,该图没有显示组(我需要在某个时候更新它)。

我希望这有帮助。

这是上面描述的 RBAC 的图表

2019 年 4 月 7 日更新 根据@Brent 的反馈(谢谢)...删除了对先前答案的不必要引用,并解释了@CodingSoft 提供的“夜总会”隐喻的原始基础,以便让这个答案可以理解而无需阅读其他答案。

于 2018-10-24T23:06:01.883 回答
55

我不完全同意 Emran 的回答

[Authorize(Roles="Sale")]

很天真

问题是如何

  [Authorize(Roles="CustomerCreator")]

不同于

 [ClaimAuthorize(Permission="CanCreateCustomer")]

如果两者都一样好,为什么我们需要索赔?

我认为因为

与角色相比,声明概念更通用

在上面示例的上下文中,我们可以说“CustomerCreator”是“Asp.NETroleProvider”提供的“角色”类型的声明

索赔的其他示例。

  1. “AAA”是“MYExamSite.com”提供的“MYExamSite.Score”类型声明

  2. “Gold”是“MYGYMApp”提供的“MYGYM.Membershiptype”类型的声明

于 2015-04-10T09:37:10.140 回答
49

公认的答案似乎将 Roles 定位为一个钝器,将 Claims 定位为一个灵活的工具,但在其他方面使它们看起来几乎相同。不幸的是,这种定位不利于索赔的概念,并且可能从根本上反映了对其目的的轻微误解。

角色仅在隐式范围内存在并且有意义。通常这是一个应用程序或组织范围(即角色=管理员)。另一方面,任何人都可以“提出”索赔。例如,Google 身份验证可能会产生包含用户“电子邮件”的声明,从而将该电子邮件附加到身份。Google 提出声明,应用程序选择是否理解并接受该声明。应用程序本身随后可能会附加一个名为“authenticationmethod”的声明(如 ASP.NET MVC Core Identity 所做的那样),其值为“Google”。每个声明都包含一个范围,以便可以识别声明是否具有外部、本地或两者(或根据需要更细粒度)的含义。

关键点是所有声明都明确附加到一个身份并包含一个明确的范围。这些声明当然可以用于授权 - 并且 ASP.NET MVC 通过 Authorize 属性提供支持,但这不是声明的唯一或必要的主要目的。它当然不能将它与角色区分开来,角色可以以完全相同的方式用于本地范围的授权。

因此,人们可以选择使用角色或声明,或者两者都用于授权,并且可能不会发现两者的固有优势或劣势,只要这些角色和声明是本地范围的。但是,例如,如果授权依赖于外部身份声明,那么角色将是不够的。您必须接受外部声明并将其转换为本地范围的角色。这不一定有什么问题,但它引入了一层间接性并丢弃了上下文。

于 2016-08-23T18:40:21.707 回答
10

RBAC 和 CBAC 之间的根本是:

RBAC:必须将用户分配给角色才能被授权执行操作。

CBAC:用户必须有一个正确值的声明,正如应用程序所期望的那样,才能被授权。基于声明的访问控制编写起来很优雅,并且更易于维护。

除此之外,声明是由您的应用程序(依赖方)信任的发行授权服务(安全服务令牌 STS)向应用程序发出的

于 2016-08-26T08:15:36.313 回答
9

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

声明不是主体可以做的事情。例如,您可能拥有由当地驾驶执照颁发机构颁发的驾驶执照。您的驾驶执照上有您的出生日期。在这种情况下,索赔名称将是 DateOfBirth,索赔值将是您的出生日期,例如 1970 年 6 月 8 日,颁发者将是驾驶执照颁发机构。最简单的基于声明的授权检查声明的值并允许基于该值访问资源。例如,如果您想进入夜总会,授权过程可能是:

在授予您访问权限之前,门禁安全人员将评估您的出生日期声明的价值以及他们是否信任颁发者(驾驶执照颁发机构)。

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

基于角色的授权

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

于 2017-12-31T13:03:08.597 回答
8

更广泛地说,您应该考虑基于属性的访问控制 (ABAC)。RBAC 和 ABAC 都是由美国国家标准与技术研究院 NIST 定义的概念。另一方面,CBAC是微软推出的一种模型,与ABAC非常相似。

在这里阅读更多:

于 2014-04-14T14:22:26.000 回答
7

角色只是声明的一种。像这样,可以有许多其他的声明类型,例如用户名是声明类型之一

于 2016-10-13T12:45:56.037 回答
4

如果你想要一个现实生活中的例子;

您有一个学校系统,教师可以登录并查看他们的学生。这些教师处于“教师”角色。但是我们不希望所有的老师都看到所有的学生,所以我们需要用他们的主张来区分相同级别的人。

  1. Mary - 数学老师(声称:数学) -> 只能看到数学学生
  2. 约翰-物理老师(声称:物理)->只能看到物理学生
  3. 亚当 - 物理和化学老师(声称:物理,化学) - >可以看到物理和化学的学生。

虽然这三位老师都是老师的角色,但他们只能看到学生的相应要求

还有一位名叫迈克的校长:

  1. Mike - 校长(角色:管理员,声明:n/a)-> 可以查看和管理所有学生,因为他处于管理员角色,无论他没有任何声明。

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

于 2020-10-29T04:14:19.077 回答
3

也可以以声明方式管理角色。

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

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

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

因为您建立在已经有效的基础之上,所以您不会撤消现有的授权过程。你只需要几个表来实现这种方法

于 2017-12-06T22:16:54.973 回答
2

我认为这个问题可以从数据库的角度来回答。如果您注意到此植入中涉及的表格,您会发现以下内容

  1. AspNetUsers :每个用户都有一行,其中包含所有用户所需的所有属性,例如电子邮件,地址电话,密码.....
  2. AspNet角色;根据应用需求定义不同的角色,如 GM、CTO、HRM、ADMIN、EMP。每个角色定义的是根据应用程序的需要。
  3. AspNetUserRoles:每一行链接 AspNetUsers 和 AspNetRoles,有效链接一个用户和多个角色。
  4. AspNetUserClaims:每一行都有 AspNetUsers 的键和一个类型和值。因此有效地为每个用户添加一个可以在运行时添加/删除的属性。

可以在用户/应用程序生命周期的某一时刻调整此表的使用,以满足特定需求。

考虑“采购经理”(PM)的早期阶段,我们可以有三种方法

  1. 应用程序用一行填充 AspNetUserRoles 以授予“PM”购买权。要发出任意数量的采购订单,用户只需要“PM”角色。

  2. 应用程序用一行填充 AspNetUserRoles 以授予“PM”购买权,并填充 AspNetUserClaims 类型为“Purchasing Amount”类型的声明和“<1000”值以设置金额限制。要发出采购订单,用户需要有'PM'并且订单金额小于索赔类型'采购金额'的索赔价值。

  3. 应用程序使用 TYPE 'Purchasing Amount' 类型和“<1000”值的声明填充 AspNetUserClaims。任何用户都可以发出采购订单,只要金额小于该用户的索赔类型“采购金额”的索赔价值。

可以注意到,基于角色的权限是粗粒度的刚性权限,从系统管理的角度来看,这将简化应用程序用户的生活。但是,从业务需求的角度来看,它会限制用户的能力。另一方面,基于声明的权利非常精细,需要分配给每个用户。基于索赔将业务推到极限,但会使系统管理非常复杂。

于 2018-04-03T12:04:57.397 回答
1

另一个需要考虑的选项是 ABAC。

基于属性的访问控制采用不同的方法,根据每个人的属性、他们请求的资源以及他们发出请求的环境授予用户访问权限。

ABAC 的主要好处是您可以精细控制每个用户的权限。例如,使用 ABAC,您可以授予人力资源应用程序的用户仅导出他们负责的区域的人事报告的权限。由于该模型旨在扩展到任意数量的属性和权限,因此在 ABAC 中构建更多动态权限通常更容易。

这里总结了差异的好文章https://cerbos.dev/blog/the-hidden-costs-of-user-authorization

于 2021-08-19T10:16:57.830 回答