Answers:
我将尝试展示如何在ASP.NET MVC上下文中受益于基于声明的访问控制。
使用基于角色的身份验证时,如果您有创建客户的操作,并且希望具有“销售”角色的人员能够执行此操作,则可以编写如下代码:
[Authorize(Roles="Sale")]
public ActionResult CreateCustomer()
{
return View();
}
后来,您意识到有时候,具有“市场营销”角色的人应该能够创建客户。然后,您像这样更新Action方法
[Authorize(Roles = "Sale", "Marketing")]
public ActionResult CreateCustomer()
{
return View();
}
现在,您意识到,有些市场营销人员不一定能够创建客户,但是不可能为市场营销人员分配其他角色。因此,您被迫允许所有营销人员创建客户。
您发现了另一个问题,只要您决定允许营销人员创建客户,就必须更新所有MVC动作方法的Authorize属性,编译应用程序,进行测试和部署。几天后,您决定不执行市场营销,但应该允许其他角色来执行此任务,因此您在代码库中进行搜索,并从Authorize属性中删除所有“ Marketing”,然后在Authorize属性中添加新的角色名称。健康的解决方案。到那时,您将意识到需要基于权限的访问控制。
基于权限的访问控制是一种为各种用户分配各种权限并检查用户是否具有在运行时根据代码执行操作的权限的方式。在为各种用户分配了各种权限后,您意识到如果用户具有诸如“ Facebook用户”,“长期用户”等属性,则需要允许某些用户执行某些代码。让我举个例子。假设您要允许访问特定页面(如果用户使用Facebook登录)。现在,您会为该用户创建权限“ Facebook”吗?不,“ Facebook”听起来不像是许可。可以 ?相反,这听起来像是一个要求。同时,权限听起来也像是Claim !!因此,最好检查索赔并允许访问。
现在,让我们回到基于声明的访问控制的具体示例。
您可以定义一些声明,例如:
“ CanCreateCustomer”,“ CanDeleteCustomer”,“ CanEditCustomer”等。
现在,您可以像这样装饰您的操作方法:
[ClaimAuthorize(Permission="CanCreateCustomer")]
public ActionResult CreateCustomer()
{
return View();
}
(请注意,[ClaimAuthorize(Permission =“ CanCreateCustomer”)]可能未内置在MVC类库中,我仅作为示例显示,您可以使用具有此类Attribute类定义的某些类库)
现在,您可以看到,CreateCustomer操作方法将始终需要权限'CanCreateCustomer',并且永远不会更改或几乎不会更改。因此,在数据库中,您将创建一个权限(声明)表和用户权限关系表。在管理面板中,您可以为每个可以执行操作的用户设置权限(声明)。您可以将“ CanCreateCustomer”权限(声明)分配给您喜欢的任何人,只有允许的用户才能创建客户,允许的用户只能创建客户,而不能创建其他用户(除非您将其他权限分配给同一用户)。
此安全模型为您提供了简洁的代码实践。此外,在编写操作方法时,您不必考虑谁可以使用此方法,而始终可以确保使用此方法的任何人都将获得管理员给予的适当许可(声明)。然后,管理员可以决定谁将能够执行操作。不是您作为开发人员。这就是您的业务逻辑与安全逻辑分离的方式。
每当有人登录时,您的应用程序都会检查该用户可用的任何权限,并且该权限(声明)集将作为当前登录用户的其他属性(通常,声明集存储为已登录用户的Cookie),因此您不必一直检查数据库的权限集。最重要的是,如果您应用基于声明的访问而不是基于角色的访问,则可以在应用程序中更好地控制安全逻辑。实际上,角色也可以视为索偿。
如果您的应用程序是一个很小的应用程序,其中只有两个角色:Customer和Admin,并且除了您在应用程序中应做的工作之外,Customer没有机会做其他任何事情,那么也许基于角色访问控制将达到目的,但是随着您的应用程序的增长,您将开始感觉到需要基于声明的访问控制。
CanCreateCustomer, CanViewAdCampaigns
我现在已经实现了很多次安全模型,并且不得不不得不围绕这些概念进行研究。做了很多次,这就是我对这些概念的理解。
什么是角色
角色=该工会的用户和权限。
一方面,角色是权限的集合。我喜欢称它为权限配置文件。定义角色时,基本上是在该角色中添加了一堆权限,因此从某种意义上说,角色是权限配置文件。
另一方面,角色也是用户的集合。如果我将Bob和Alice添加到角色“经理”,则“经理”现在包含两个用户的集合,有点像一个组。
事实是,角色既是用户的集合又是权限的集合。在视觉上,可以将其视为维恩图。
什么是团体
组=用户集合
“组”严格来说是用户的集合。组和角色之间的区别在于,角色也具有权限的集合,而组仅具有用户的集合。
什么是权限
权限=主题可以做什么
什么是权限集
权限集=权限集合
在强大的RBAC系统中,权限也可以像“用户”一样分组。组是仅用户的集合,而权限集仅是权限的集合。这使管理员可以一次将整个权限集合添加到角色。
用户,组,角色和权限如何组合在一起
在功能强大的RBAC系统中,可以将用户单独添加到角色中以创建角色中的用户集合,也可以将组添加到角色中以一次将用户集合添加到角色中。无论哪种方式,角色都可以通过单独添加角色或通过将组添加到角色或通过将用户和组的混合添加到角色来获取其用户集合。可以用相同的方式来考虑权限。
可以将权限分别添加到角色,以在角色内部创建权限集合,也可以将权限集添加到角色。最后,可以将权限和权限集混合添加到角色。无论哪种方式,角色都可以通过单独添加权限或通过向角色添加权限集来获取其权限集合。
角色的全部目的是使用户嫁给权限。因此,角色是用户和权限的联合。
什么是索赔
声明=主题“是”
声明不是许可。如前面的答案所指出的,主张是主体“是”而不是主体“可以做什么”。
声明并不能替代角色或权限,它们是可以用来做出授权决策的其他信息。
何时使用版权声明
我发现,在无法将用户添加到角色或决策不是基于用户与权限的关联时,需要做出授权决策时,声明非常有用。Facebook用户的示例导致了这种情况。Facebook用户可能不是添加到“角色”中的人……他们只是通过Facebook进行身份验证的一些访问者。尽管它不完全适合RBAC,但它是做出授权决定的一条信息。
@CodingSoft在上一个答案中使用了夜总会的隐喻,我想对此进行扩展。在该答案中,以“驾驶执照”为例,其中包含一组索赔,其中“出生日期”代表其中一项索赔,“出生日期”索赔的值用于对照授权规则进行测试。颁发驾驶执照的政府是赋予索赔真实性的机构。因此,在夜总会的情况下,门旁的保镖会查看该人的驾驶执照,并通过检查该证件是否为假ID(即必须是政府颁发的有效ID)来确保它是由受信任的机构签发的,然后查看出生日期(驾驶执照上的许多索偿要求之一),然后使用该值确定此人的年龄是否足以进入俱乐部。如果是这样的话,
现在,考虑到这个基础,我现在想进一步扩展。假设夜总会所在的建筑物包含办公室,房间,厨房,其他楼层,电梯,地下室等,只有夜总会员工可以进入。此外,某些员工可能会访问其他员工可能无法访问的某些地方。例如,经理可能有权访问其他员工无法访问的办公楼层。在这种情况下,有两个角色。经理和员工。
如上所述,虽然访问者进入公共夜总会区的权利受到单个索赔的授权,但员工需要通过“角色”来访问其他非公共受限房间。对于他们来说,驾驶执照还不够。他们需要的是员工徽章,他们可以扫描员工徽章进入大门。某个地方有一个RBAC系统,该系统授予“经理角色”中的徽章访问顶层,并授予“员工角色”中的徽章访问其他房间。
如果出于某种原因需要通过角色添加/删除某些房间,则可以使用RBAC进行此操作,但这并不适合索赔。
软件权限
将角色编码到应用程序中是个坏主意。这会将角色的用途硬编码到应用程序中。该应用程序应该具有的只是功能类似于功能标志的权限。在通过配置使功能标记可访问的地方,通过用户安全上下文可以访问权限,该用户安全上下文是由DISTINCT的权限集合(从放置用户的所有角色收集的)派生的。这就是我所说的“有效权限”。该应用程序应该只显示一个菜单对功能/动作的可能许可。RBAC系统应该完成通过角色将这些权限与用户结合的工作。这样,就无需对角色进行硬编码,并且只有在删除权限或添加新权限后,权限才会发生变化。将权限添加到软件后,就永远不能更改它。仅应在必要时将其删除(即,在新版本中不再使用某个功能时),并且只能添加新功能。
最后一点。
格兰特vs丹尼
强大的RBAC系统甚至CBAC系统都应在Grants和Denials之间进行区分。
向角色添加权限应随GRANT或DENY一起提供。选中权限后,应将所有已授予的权限添加到“有效权限的用户”列表中。然后,在完成所有这些操作之后,DENIED Permissions列表应使系统从Effective Permissions列表中删除这些权限。
这使管理员可以“调整”主题的最终许可。最好也可以将权限直接添加到用户。这样,您可以将用户添加到管理员角色,他们可以访问所有内容,但是由于用户是男性,因此您可能想拒绝访问女士洗手间。因此,您可以将男性用户添加到管理员角色,并使用DENY向用户对象添加权限,这样它就只占用女士房间的访问权限。
实际上,这将是一个不错的选择。如果用户的索赔为“ gender = male”,则处于“管理者”角色可访问所有房间,但女士洗手间也要求Claim性别=女,而男士洗手间要求Claim性别= male。通过这种方式,不必为男性用户配置DENY权限,因为Claim强制使用单个授权规则为每个人处理该权限。但是,可以采用任何一种方式来完成。
关键是,使用“拒绝权限”,由于可以实现异常,因此使角色的管理更加容易。
下面是我很久以前制作的图表,显示了RBAC模型。我没有索赔的图形,但您可以想象它们只是附加到用户的属性,无论它们位于何处。另外,该图未显示“组”(我需要在某个时候进行更新)。
我希望这有帮助。
更新于2019年4月7日 基于@Brent的反馈(谢谢)...删除了对先前答案的不必要引用,并解释了@CodingSoft提供的“夜总会”隐喻的原始基础,以使此答案易于理解而无需阅读其他答案。
我不同意Emran的回答
[Authorize(Roles="Sale")]
天真
问题是
[Authorize(Roles="CustomerCreator")]
与...不同
[ClaimAuthorize(Permission="CanCreateCustomer")]
如果两者都一样好,为什么我们需要索赔?
我认为是因为
在以上示例的上下文中,我们可以说“ CustomerCreator”是“ Asp.NETroleProvider”提供的“ role”类型的声明
索赔的其他示例。
“ AAA”是“ MYExamSite.com”提供的“ MYExamSite.Score”类型的声明。
“金”是“ MYGYMApp”提供的“ MYGYM.Membershiptype”类型的声明
公认的答案似乎将“角色”定位为钝器,将“声明”定位为灵活的工具,但否则会使它们看起来几乎完全相同。不幸的是,这种定位不利于权利要求的概念,并且可能从根本上反映出对权利要求的误解。
角色仅在隐式范围内存在并有意义。通常,这是一个应用程序或组织范围(即Role = Administrator)。另一方面,任何人都可以“提出”主张。例如,Google身份验证可能会产生包括用户“电子邮件”的声明,从而将该电子邮件附加到身份上。Google提出索赔,应用程序选择是否理解并接受该索赔。应用程序本身随后可能会附加一个名为“ authenticationmethod”(如ASP.NET MVC Core Identity那样)的声明,其值为“ Google”。每个声明都包含一个范围,以便可以识别一个声明是在外部,本地还是在两者(或根据需要更细化)的含义。
关键是所有声明都明确附加到身份上并包括明确范围。这些声明当然可以用于授权-ASP.NET MVC通过Authorize属性提供了对此的支持,但这不是声明的唯一或必要的甚至主要目的。当然,它与Roles并无区别,Roles可以以完全相同的方式用于本地范围的授权。
因此,一个人可以选择使用“角色”或“声明”,或同时使用两者,以进行授权,并且只要这些角色和声明在本地范围内,就可能不会发现两者的固有优势或劣势。但是,例如,如果授权取决于外部身份声明,那么角色将不足。您将必须接受外部声明并将其转换为本地范围的角色。这不一定有什么问题,但是它引入了一个间接层,并丢弃了上下文。
更广泛地说,您应该考虑基于属性的访问控制(ABAC)。RBAC和ABAC都是美国国家标准技术研究院NIST定义的概念。另一方面,CBAC是Microsoft推动的一种模型,与ABAC非常相似。
在这里阅读更多:
RBAC和CBAC之间的基本原则是:
RBAC:必须将用户分配给角色,以授权其执行操作。
CBAC:用户必须具有应用程序期望的具有正确值的声明才能获得授权。基于声明的访问控制易于编写且易于维护。
此外,索赔是由您的应用程序(信任方)信任的发行授权服务(安全服务令牌STS)向应用程序发出的
角色只是索赔的一种。这样,可以有许多其他声明类型,例如,用户名是声明类型之一
在决定哪种方法最好之前,首先分析要求进行身份验证的内容很重要。在下面的Microsoft文档中,它说:“索赔不是主体所能做的。例如,您可能具有由本地驾驶执照颁发机构颁发的驾驶执照。您的驾驶执照上载有您的生日。在这种情况下,索赔名称将为DateOfBirth,索赔值将为您的生日,例如1970年6月8日,签发者将为驾驶执照授权机构。基于索赔的授权,最简单的是检查索赔的值并允许访问基于该值的资源。例如,如果您想使用夜总会,则授权过程可能是:6“
从此示例中,我们可以看到,使用基于索赔的授权访问附近的俱乐部与在夜总会工作的工作人员要求的授权类型不同,在这种情况下,夜总会的工作人员将要求夜总会访客不需要的基于角色的授权,因为夜总会访客在夜总会都有共同的目的,因此在这种情况下,基于索赔的授权适合夜总会访客。
基于角色的授权 https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/roles 10/14/2016创建身份后,它可能属于一个或多个角色。例如,Tracy可能属于管理员和用户角色,而Scott可能仅属于用户角色。如何创建和管理这些角色取决于授权过程的后备存储。角色通过ClaimsPrincipal类上的IsInRole方法向开发人员公开。
基于声明的授权 https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/claims 2016年10月14日,创建身份后,可能会分配一个或多个由受信任方发出的声明。声明是名称值对,表示主题是什么,而不是主题可以做什么。例如,您可能具有由本地驾驶执照颁发机构颁发的驾驶执照。您的驾驶执照上载有您的出生日期。在这种情况下,索赔名称将为DateOfBirth,索赔值将为您的出生日期,例如1970年6月8日,颁发者将为驾驶执照颁发机构。最简单的基于声明的授权检查声明的值,并允许基于该值访问资源。例如,如果您想使用夜总会,则授权过程可能是:
门安全员将在授予您访问权限之前评估您的出生日期声明的价值以及他们是否信任颁发者(驾驶执照颁发机构)。
身份可以包含具有多个值的多个声明,并且可以包含同一类型的多个声明。
也可以以声明的方式管理角色。
代替创建反映业务角色的授权角色,而是创建反映操作角色的角色,例如CreateCustomer,EditCustomer,DeleteCustomer。根据需要注释方法。
将个人映射到一组动作角色并不是一件容易的事,尤其是随着角色列表的增加。因此,您需要以较低的粒度级别(例如,销售,市场营销)管理业务角色,并将业务角色映射到所需的操作角色。即,将用户添加到业务角色,并将其映射到现有授权表中所需的(操作)角色。
您甚至可以覆盖业务角色,然后直接将人员添加到操作角色。
因为您是在已有的基础上构建的,所以您不会撤消现有的授权过程。您只需要几个表即可实现此方法
我认为这个问题可以从数据库中得到答案。如果您注意到此植入过程涉及的表格,您将发现以下内容
可以在用户/应用程序生命周期的某一时刻调整此表的用法,以匹配特定需求。
考虑“采购经理”(PM)的早期阶段,我们可以采用三种方法
应用程序用一行填充AspNetUserRoles来授予“ PM”购买权。要发出任意数量的采购订单,用户仅需要“ PM”角色。
应用程序在第一行中填充AspNetUserRoles以授予“ PM”购买权,然后在AspNetUserClaims中填充“购买金额”类型和“ <1000”值的索赔以设置金额限制。要发出采购订单,用户需要具有“ PM”并且订单金额小于索赔类型“ Purchasing Amount”的索赔额。
应用程序使用“采购金额”类型和“ <1000”值的声明填充AspNetUserClaims。只要该用户的金额小于索赔类型“采购金额”的索赔值,任何用户都可以发出采购订单。
可以注意到,基于角色的是粗暴的刚性权限,从系统管理的角度来看,这将简化应用程序用户的生活。但是,从业务需求的角度来看,这将限制用户的能力。另一方面,基于声明的权利非常好,需要分配给每个用户。基于索赔的做法也会将业务推到极限,但是会使系统管理非常复杂。
基于角色的访问控制(RBAC)
在您的组织中,您可能扮演以下角色
雇员
经理
人力资源
根据登录用户所属的一个或多个角色,您可以授权也可以不授权访问应用程序中的某些资源。由于我们使用角色进行授权检查,因此通常称为基于角色的访问控制(RBAC)或基于角色的授权。
在ASP.NET Core中,为了实现基于角色的授权,我们使用带有Roles参数的Authorize属性。
[Authorize(Roles = "Admin")]
public class AdministrationController : Controller
{
}
基于声明的访问控制(CBAC)
什么是索赔? 声明是一个名称/值对。这实际上是有关用户的一条信息,而不是用户可以做什么和不能做什么。例如,用户名,电子邮件,年龄,性别等都是声明。在应用程序中如何使用这些声明进行授权检查取决于您的应用程序业务和授权要求。
例如,如果您正在建立一个员工门户,则在性别声明值为女性的情况下,可以允许已登录的用户申请产假。同样,如果您正在构建电子商务应用程序,则可以在年龄索赔值大于或等于18时允许登录用户提交订单。
索赔基于政策。我们创建了一项保单,并在该保单中包含一个或多个声明。然后将该策略与Authorize属性的策略参数一起使用,以实现基于声明的授权。
[Authorize(Policy = "DeleteRolePolicy")]
public async Task<IActionResult> DeleteRole(string id)
{
}