我将尝试以通俗易懂的方式解释基于角色/声明/权限的访问控制概念。这里我将呈现的代码片段是伪代码,可能无法编译。
什么是角色?
可以将角色看作职称,例如“销售经理”、“市场营销经理”、“管理员”等。
什么是声明?
声明可以比角色更广泛。你可以将声明视为标签。例如,你可以将一个人标记为“友好”、“健谈”、“欧洲人”、“摄影师”、“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属性。我不完全同意Emran的回答。
[Authorize(Roles="Sale")]
太天真了
问题在于如何解决
[Authorize(Roles="CustomerCreator")]
与...不同
[ClaimAuthorize(Permission="CanCreateCustomer")]
如果两者一样好,我们为什么需要声明呢?RBAC和CBAC之间的基本区别在于:
RBAC:用户必须被分配到一个角色才能被授权执行某个操作。
CBAC:用户必须具有正确值的声明,以便按照应用程序的期望进行授权。基于声明的访问控制编写起来更加优雅,更易于维护。
此外,声明是由一个受信任的授权服务(安全服务令牌STS)向应用程序发放的,该应用程序是信任该服务的(依赖方)。
在决定哪种方法最好之前,首先需要分析需要身份验证的内容是什么。来自基于声明的授权:
一个声明不代表主题能够做什么。例如,您可能拥有由当地驾照管理机构颁发的驾驶执照。您的驾驶执照上有您的出生日期。在这种情况下,声明名称将是DateOfBirth,声明值将是您的出生日期,例如1970年6月8日,发行人将是驾驶执照管理机构。基于声明的授权最简单的方式是检查声明的值并根据该值允许访问资源。例如,如果您想要进入一个夜总会,授权过程可能如下:
门卫将评估您的出生日期声明的价值以及他们是否信任发行人(驾驶执照管理机构),然后才会允许您进入。
从这个例子中,我们可以看到通过基于声明的授权访问夜总会与工作在夜总会的员工所需的授权类型有很大的不同,在这种情况下,夜总会的工作人员将需要基于角色的授权,而夜总会的访客则不需要。因为夜总会的访客在夜总会有共同的目的,因此在这种情况下,基于声明的授权适合夜总会的访客。
从基于角色的授权:
当创建身份时,它可能属于一个或多个角色。例如,Tracy 可能属于管理员和用户角色,而 Scott 只属于用户角色。如何创建和管理这些角色取决于授权过程的后备存储。角色通过 ClaimsPrincipal 类上的 IsInRole 方法向开发人员公开。
如果您需要一个现实生活中的例子;
你有一个学校系统,老师可以登录并查看他们的学生。这些老师属于“教师”角色。但我们不希望所有老师都能看到所有学生,因此我们需要通过他们的身份来区分同一级别的人。
虽然这三位老师都属于教师角色,但他们只能看到与其对应的身份的学生。
还有一个名叫Mike的校长:
如果我们需要区分管理员级别的人,我们可以为每个管理员分配相关的身份。
角色仅是声明的一种类型。同样地,还有许多其他声明类型,例如用户名就是其中之一。
你还可以以一种声明方式来管理角色。
不要创建反映业务角色的授权角色,而是创建反映操作角色的角色,例如CreateCustomer、EditCustomer、DeleteCustomer。根据需要注释方法。
将个人映射到一组操作角色并不是一件简单的事情,特别是当角色列表变得越来越大时。因此,您需要在更低级别的粒度(例如销售、营销)上管理业务角色,并将业务角色映射到所需的操作角色。也就是说,将用户添加到业务角色中,它会将它们映射到现有授权表中所需的(操作)角色。
您甚至可以覆盖业务角色,直接将人员添加到操作角色中。
由于建立在已经起作用的基础上,您不会撤消现有的授权过程。只需要几个额外的表来实现这种方法即可。