我对在 ASP.NETIdentity
中使用 claims
完全陌生,并且想了解使用 Roles and/or Claims
的最佳做法。
在阅读完所有这些之后,我仍然有类似的问题......
问:我们不再使用角色了吗?问:如果是这样,为什么仍然提供角色?问:我们应该只使用声明吗?问:我们应该一起使用角色和声明吗?
我最初的想法是我们“应该”一起使用它们。我将 Claims
视为他们支持的 Roles
的子类别。
例如:角色:会计索赔:CanUpdateLedger、CanOnlyReadLedger、CanDeleteFromLedger
问:它们是否打算相互排斥?问:还是只进行索赔并“完全符合”您的索赔要求?问:那么这里的最佳实践是什么?
示例:同时使用角色和声明当然,您必须为此编写自己的属性逻辑......
[Authorize(Roles="Accounting")]
[ClaimAuthorize(Permission="CanUpdateLedger")]
public ActionResult CreateAsset(Asset entity)
{
// Do stuff here
return View();
}
示例:完全限定您的索赔
[ClaimAuthorize(Permission="Accounting.Ledger.CanUpdate")]
public ActionResult CreateAsset(Asset entity)
{
// Do stuff here
return View();
}
角色是一个符号类别,它将共享相同级别安全权限的用户聚集在一起。基于角色的授权需要首先识别用户,然后确定用户被分配到的角色,最后将这些角色与被授权访问资源的角色进行比较。
相反,声明不是基于组的,而是基于身份的。
当一个身份被创建时,它可能被分配一个或多个由受信任方发布的声明。声明是一个名称值对,它表示主题是什么,而不是主题可以做什么。
稍后,安全检查可以根据一个或多个声明的值确定访问资源的权利。
您可以同时使用这两种类型,或者在某些情况下使用一种类型,在其他情况下使用另一种类型。这主要取决于与其他系统的互操作以及您的管理策略。例如,经理管理分配给某个角色的用户列表可能比管理分配了特定声明的用户更容易。声明在 RESTful 场景中非常有用,您可以将声明分配给客户端,然后客户端可以呈现声明以进行授权,而不是为每个请求传递用户名和密码。
正如@Claies 完美解释的那样,声明可能更具描述性,并且是一种深入的角色。我认为它们是您的角色 ID。我有一个健身房ID,所以我属于会员角色。我也在上跆拳道课,所以我为他们申请了跆拳道身份证。我的申请需要声明一个新角色以符合我的会员权利。相反,我有我所属的每个组类的 id,而不是许多新的成员类型。这就是为什么索赔更适合我。
有一个很棒的 Barry Dorrans 解释视频,谈论使用声明而不是角色的优势。他还指出,角色仍然在 .NET 中以实现向后兼容性。该视频对声明、角色、策略、授权和身份验证的工作方式提供了非常丰富的信息。
您可以在这里找到它:ASP.NET Core Authorization with Barr Dorrans
几十年来使用了各种身份验证和授权技术,我当前的 MVC 应用程序使用以下方法。
声明用于所有授权。用户被分配一个角色(多个角色是可能的,但我不需要这个) - 更多如下。
按照惯例,使用 ClaimsAuthorize 属性类。由于大多数控制器操作都是 CRUD,因此我在代码优先数据库生成中有一个例程,它迭代所有控制器操作并为读取/编辑/创建/删除的每个控制器操作属性创建声明类型。例如从,
[ClaimsAuthorize("SomeController", "Edit")]
[HttpPost]
为了在 MVC 视图中使用,基本控制器类呈现视图包项
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// get user claims
var user = filterContext.HttpContext.User as System.Security.Claims.ClaimsPrincipal;
if (user != null)
{
// Get all user claims on this controller. In this controler base class, [this] still gets the descendant instance type, hence name
List<Claim> claims = user.Claims.Where(c => c.Type == this.GetType().Name).ToList();
// set Viewbag with default authorisations on this controller
ViewBag.ClaimRead = claims.Any(c => c.Value == "Read");
ViewBag.ClaimEdit = claims.Any(c => c.Value == "Edit");
ViewBag.ClaimCreate = claims.Any(c => c.Value == "Create");
ViewBag.ClaimDelete = claims.Any(c => c.Value == "Delete");
}
base.OnActionExecuting(filterContext);
}
对于网站菜单和其他非控制器操作,我有其他主张。例如,用户是否可以查看特定的货币字段。
bool UserHasSpecificClaim(string claimType, string claimValue)
{
// get user claims
var user = this.HttpContext.User as System.Security.Claims.ClaimsPrincipal;
if (user != null)
{
// Get the specific claim if any
return user.Claims.Any(c => c.Type == claimType && c.Value == claimValue);
}
return false;
}
public bool UserHasTradePricesReadClaim
{
get
{
return UserHasSpecificClaim("TradePrices", "Read");
}
}
那么角色在哪里适合呢?
我有一个表,将角色链接到一组(默认)声明。设置用户授权时,默认是给用户他们角色的声明。每个用户可以拥有比默认更多或更少的声明。为了简化编辑,声明列表按控制器和操作(连续)显示,然后列出其他声明。按钮与一些 Javascript 一起使用来选择一组操作,以最大限度地减少选择声明所需的“点击”。保存时,将删除用户声明并添加所有选定的声明。 Web 应用程序仅加载一次声明,因此任何更改都必须提示重新加载此静态数据。
因此,管理人员可以选择每个角色中的哪些声明以及用户在将其设置为角色后拥有哪些声明以及这些默认声明。该系统只有少量用户,因此管理这些数据很简单
ClaimsAuthorize
的引用。那不再存在并且我必须创建自己的 Attribute
吗?谢谢
要了解角色和声明之间的区别,您必须面对角色的限制并感受声明是如何解决这些问题的,所以让我给您两个场景来认识声明的力量,其中角色无法解决这些问题:
1-您的网站有两个模块(页面、服务..等)第一个模块适用于儿童(18 岁以下)另一个适用于成人(18 岁以上)您的用户身份有生日声明
您需要为此声明创建一个策略,以便每个模块的授权都将基于此值,如果用户的年龄超过 18 岁,那么他可以进入成人模块,而不是在此年龄之前。
角色是您可以拥有或不拥有角色的布尔数据类型,它没有多个值。
2-您的站点具有角色用户,并且您希望阻止用户访问以进行一些维护而不更改代码。
在声明中,您可以创建一个 UnderConstrain 策略,如果该策略为 true,则用户无法查看页面,为角色用户授予属性授权。
在撰写此答案时,我们正处于“.NET 5.0”,而“.NET 6.0”指日可待。这是我对所见所闻的理解:
问:我们不再使用角色了吗?
是的,你不应该再使用角色了(至少不是你在以前的框架中使用的方式。
问:如果是这样,为什么仍然提供角色?
使升级项目更容易/更快?
问:我们应该只使用声明吗?
是的。但请务必查看@Jonathan Ramos 的答案中发布的视频。
问:我们应该一起使用角色和声明吗?
不可以,但是您当然可以将角色放入声明中,但请务必升级您的项目以仅使用声明。
而且您不必编写自己的属性,您应该为此使用策略,因为这是较新框架的方式。如果您需要自己的属性,那么您“做错了”,只需创建自己的需求(处理程序),这就是整个“新”政策的全部内容。在当前框架中,ClaimAuthorize 属性甚至不再可用。
不定期副业成功案例分享