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

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

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

61902 次浏览

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

什么是角色?

角色可以被认为是工作头衔。比如“销售经理”、“市场经理”、“行政经理”;等。

索赔是什么?

声明可以比角色更广泛。您可以将Claim看作一个TAG。例如,你可以给一个人贴上“友好”、“健谈”、“欧洲人”、“摄影师”、“18岁的成年人”的标签。等。从技术上讲,角色也可以被认为是一种要求。

基于角色的访问控制

< p >非常简单。不使用文字,让我们看一些例子。 假设,您允许通过检查角色访问网站上的某些页面。这样的:< / p >
    [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();
}

请注意,我们允许根据用户声称的WHO访问页面,而不是检查角色。

RBAC vs CBAC

好了,现在,如果你问基于角色的访问控制或基于声明的访问控制的好处是什么,那么,想想这个页面。检查“18岁以上成年人”的声明不是更直观吗?当你决定是否允许用户访问该页面时?总之,使用Claims,您可以在用户比较角色中创建更多段。从抽象的意义上讲,所有的角色都可以是声明,但是声明不能被认为是角色。

基于权限的访问控制

在允许权限查看页面时,您应该考虑基于权限的访问控制,而不是检查role或claim。让我给你看一些痛点。

当你使用基于角色的身份验证时,如果你有一个创建客户的动作,并且你希望处于“销售”角色的人应该能够做到这一点,那么你写这样的代码:

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

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

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

现在,您意识到一些营销人员肯定不能创建客户,但不可能为这些营销人员分配不同的角色。所以,你不得不让所有的营销人员去创造客户。

你发现了另一个问题,任何时候你决定允许营销人员创建客户,你必须更新你所有的MVC动作方法授权属性,编译你的应用程序,测试和部署。几天后,你决定不让市场部的角色来做这个任务,而是让其他角色来做,所以你在代码库中搜索,从授权属性中删除所有的“marketing”,并在授权属性中添加你的新角色名……这不是一个健康的解决方案。在这一点上,您将意识到需要基于权限的访问控制。

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

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

“cancreatecustomer”,“candeletecustomer”,“CanEditCustomer"等。

现在,你可以像这样装饰你的动作方法:

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

请注意,[Authorize(Permission="CanCreateCustomer")]可能不会 被构建到MVC类库中,我只是作为一个抽象意义上的例子来展示。可以有一个NuGet包,它将在Authorize类中具有Permission属性

现在,你可以看到,CreateCustomer操作方法总是需要CanCreateCustomer权限,它永远不会改变或几乎不会改变。

谁将获得许可?

您可以直接为用户分配一组权限。但是不要这样做。要做到这一点将非常困难。相反,

您可以为角色分配一组权限,也可以为Claim(推荐)分配一组权限。

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

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

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

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

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

点击此处阅读更多信息:

我不完全同意Emran的回答

[Authorize(Roles="Sale")]

是天真的

问题是如何

  [Authorize(Roles="CustomerCreator")]

不同于

 [ClaimAuthorize(Permission="CanCreateCustomer")]

如果两者都是一样的好,为什么我们需要声明?

我想是因为

声明概念比角色更通用

在上面的例子中,我们可以说“CustomerCreator”是由“Asp. Asp. creator”提供的类型为“role”的声明。NETroleProvider”

索赔的其他例子。

  1. “AAA”是类型为“MYExamSite”的索赔。分数由“MYExamSite.com”提供

  2. “黄金”是MYGYM类型的索赔。“MYGYMApp”提供的会员类型

公认的答案似乎将角色定位为钝器,而声明定位为灵活的工具,但在其他方面使它们看起来几乎相同。不幸的是,这种定位有损于索赔的概念,并可能从根本上反映出对其目的的轻微误解。

角色仅在隐式作用域中存在并有意义。通常这是一个应用程序或组织范围(即Role=Administrator)。另一方面,任何人都可以“提出”要求。例如,谷歌身份验证可能产生包含用户“电子邮件”的声明,从而将该电子邮件附加到身份。谷歌提出索赔,应用程序选择是否理解和接受该索赔。应用程序本身随后可能附加一个名为“authenticationmethod”的声明(如ASP。NET MVC核心身份做),值为“谷歌”。每个声明都包含一个范围,以便可以确定声明是否具有外部意义、本地意义或两者都有意义(或根据需要更细粒度地定义)。

关键点在于,所有声明都显式地附加到一个标识,并包括一个显式的范围。这些声明当然可以用于授权-和ASP。NET MVC通过Authorize属性提供了对此的支持,但这不是claim的唯一目的,甚至不一定是主要目的。它当然不会与role区分开来,后者可以以完全相同的方式用于本地范围的授权。

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

RBAC和CBAC的基本原理是:

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

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

除此之外,索赔由您的应用程序信任的发出授权服务(安全服务令牌STS)发出给应用程序(依赖方)。

角色只是Claim的一种类型。与此类似,还可以有许多其他声明类型,例如用户名就是声明类型之一

还可以以声明的方式管理角色。

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

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

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

因为构建在已经工作的基础上,所以不会撤销现有的授权流程。您只需要几个表就可以实现这种方法

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

主张不是主体能做什么。例如,你可能有当地驾驶执照颁发机构颁发的驾驶执照。你的驾照上有你的出生日期。在本例中,索赔名称将是出生日期,索赔值将是您的出生日期,例如1970年6月8日,而签发方将是驾驶执照颁发机构。基于声明的授权,简单来说,就是检查声明的值,并允许基于该值访问资源。例如,如果你想进入一个夜总会,授权过程可能是:

在允许你进入之前,门卫会评估你出生日期的价值,以及他们是否信任发证机构(驾驶执照机构)。

从这个例子我们可以看到,使用声明式授权访问几乎俱乐部退出不同的授权类型需要的员工在夜总会工作,在这种情况下,员工俱乐部需要一个基于角色的授权而不是必需的夜总会游客的夜总会的游客都有一个共同的目的在夜总会因此在这种情况下的声明式授权适用于夜总会游客。

基于角色授权:

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

我认为这个问题可以从数据库的角度来回答。 如果您注意到表是如何参与这个植入的,您将发现以下

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

这个表的使用可以在用户/应用程序生命周期的某个时刻进行调整,以匹配特定的需求。

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

  1. 应用程序用一行填充AspNetUserRoles以授予“PM”购买权。用户只需扮演“PM”角色,即可发出任何金额的采购订单。

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

  3. 应用程序填充AspNetUserClaims类型为“购买金额”类型,值为“<1000”。任何用户都可以发出采购订单,只要金额小于该用户的索赔类型“采购金额”的索赔值。

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

到目前为止,我已经实现了许多次安全模型,也不得不把我的脑袋绕在这些概念上。做过很多次,以下是我对这些概念的理解。

什么是角色

Role =用户和权限的联盟

一方面,Role是权限的集合。我喜欢称它为权限配置文件。当定义一个角色时,你基本上是在这个角色中添加了一堆权限,所以从这个意义上说,一个角色就是一个权限配置文件。

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

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

什么是组

组=用户的集合

“组”严格来说是用户的集合。组和角色的区别在于角色也有权限集合,而组只有用户集合。

什么是权限

权限=一个主体可以做什么

什么是权限集

权限集=权限的集合

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

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

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

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

角色的全部目的是将用户与权限结合起来。Role是Users和Permissions的UNION。

什么是索赔

Claim =主语是什么

声明不是权限。正如前面的回答所指出的,Claim是主体“是”的,而不是主体“能做”的。

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

何时使用索赔

我发现当需要做出授权决策时,当用户不能添加到角色或决策不是基于用户与权限的关联时,声明是有用的。Facebook用户的例子导致了这一点。Facebook用户可能不是被添加到“角色”的人……他们只是通过Facebook认证的访问者。尽管它不完全适合RBAC,但它是一个用于做出授权决策的信息。

@CodingSoft在之前的回答中使用了夜总会的比喻,我想扩展一下。在该回答中,以驾驶执照为例,其中包含一组索赔,其中出生日期代表其中一个索赔,而出生日期索赔的值用于测试授权规则。颁发驾驶执照的政府是赋予索赔真实性的权威机构。因此,在夜总会场景中,门口的保镖会查看这个人的驾驶执照,通过检查它是否是假身份证(即必须是有效的政府签发的身份证)来确保它是由可信的权威机构签发的,然后查看出生日期(驾驶执照上的许多声明之一),然后使用该值来确定这个人是否年龄足够大,可以进入俱乐部。如果是这样,此人通过具有有效的Claim而不是通过处于某个角色来通过授权规则。

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

正如上面所解释的那样,访客进入公共夜总会区域的权限由单一声明授权,但员工需要通过Role进入其他非公共限制房间。对他们来说,只有驾照是不够的。他们需要的是一个员工卡,他们扫描进门。在某个地方,有一个RBAC系统授予经理角色中的徽章进入顶层的权限,以及员工角色中的徽章进入其他房间的权限。

如果出于某种原因,Role需要添加/删除某些房间,可以使用RBAC来完成,但它不适合Claim。

软件中的权限

将角色编码到应用程序中是一个坏主意。这将把Role的目的硬编码到应用程序中。应用程序应该拥有的只是像功能标志一样的权限。功能标志可以通过配置访问,权限可以通过用户安全上下文访问,用户安全上下文是由从用户所处的所有角色收集的DISTINCT权限集合派生的。这就是我所说的“有效权限”。应用程序应该只提供一个菜单的功能/操作可能的权限。RBAC系统应该通过角色将这些权限与用户结合起来。这样,就不需要对角色进行硬编码,只有在删除或添加新权限时才会更改权限。一旦一个权限被添加到软件中,它就不应该被改变。它只应该在必要的时候被删除(即当一个特性在新版本中被停止时),并且只能添加新的特性。

最后一点。

Grant vs Deny

一个健壮的RBAC系统甚至CBAC系统都应该区分grant和deny。

向角色添加权限应该带有GRANT或DENY。勾选“权限”后,所有授予的权限都应添加到“有效权限”的“用户”列表中。然后,在所有这些完成后,一个DENIED Permissions列表应该会导致系统从Effective Permissions列表中删除这些Permissions。

这允许管理员“调整”主题的最终权限。如果权限也可以直接添加到用户,那就最好了。通过这种方式,您可以将用户添加到经理角色,他们可以访问所有内容,但可能您希望拒绝访问女厕所,因为用户是男性。因此,您将男性用户添加到经理角色,并使用DENY向User对象添加权限,这样它只会剥夺女士的房间访问权。

实际上,这将是Claim的一个很好的候选者。如果用户有一个声明“性别=男性”,那么在经理角色中可以访问所有房间,但女厕所也要求声明性别=女性,男厕所要求声明性别=男性。通过这种方式,不必为男性用户配置DENY权限,因为Claim实施将使用单个授权规则为每个人配置DENY权限。然而,这两种方法都可以。

关键是使用拒绝权限,可以更容易地管理角色,因为可以实现异常。

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

我希望这能有所帮助。

这是上述RBAC的示意图

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

如果你想要一个真实的例子;

你有一个学校系统,老师可以登录并看到他们的学生。那些老师属于“老师"的角色。但我们不希望所有的老师看到所有的学生,所以我们需要区分同一水平的人与他们的主张。

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

虽然这三位老师都在老师角色下,但他们只能看到对应索赔的学生。

有一个叫迈克的校长

  1. Mike -负责人(角色:管理员,索赔:无)->可以看到和管理所有的学生,因为他在管理角色下,不管他有没有任何索赔。

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

另一个可以考虑的选项是ABAC。

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

ABAC的主要好处是可以对每个用户的权限进行细粒度控制。例如,使用ABAC,您可以为人力资源应用程序的用户授予仅为他们负责的区域导出人员报告的权限。因为模型被设计成可以扩展到任意数量的属性和权限,所以在ABAC中构建更动态的权限通常更容易。

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