在 1.0 版的 ASP.NET Membership 中,IRole 接口必须有一个 string 主键。但是在 2014 年 3 月发布的 2.0 版中,他们添加了一个 IRole<TKey>,允许您指定角色的主键,只要 TKey 实现 IEquatable<TKey>。
也就是说,用于授权的开箱即用 MVC 集成点仍然依赖于由单个字符串标识的角色。因此,如果您要在那里通过 Authorize 等属性进行授权,那么您可能需要编写自己的自定义授权属性。
实现分层角色的一种方法是在您的应用程序中而不是在模型中处理它。我假设分层,您的意思是管理员拥有经理的所有权限,经理拥有与编辑者相同的权限,依此类推。您可以通过将用户添加到多个角色来实现这一点,而不必遍历建模的角色层次结构。像 db 触发器之类的东西可以做到这一点,但您也可以将其建模为代码中的业务规则。然后,如果您将页面限制为编辑器,管理员和经理也可以访问它。
另一种方法是仅授权多个角色的某些操作:
[Authorize(Roles = "Administrator, Manager, Editor")]
public ActionResult Edit()
[Authorize(Roles = "Administrator, Manager")]
public ActionResult Manage()
[Authorize(Roles = "Administrator")]
public ActionResult Admin()
我不同意您希望在复合键上标识角色。如果您想使用 Authorize 属性保护 MVC 操作,角色的 ID 需要是一个常量值,如字符串文字、int 或 Enum 值等。如果您在多个属性上键入角色,则数字您需要在属性上设置的属性数乘以 id 的每个组件中的值数。您将拥有 Manager/SiteA、Manager/SiteB 等。
相反,将属性添加到跟踪用户角色的动名词(多对多关系中的中间表)听起来可能是个好主意。为此,您将无法像@Chris Pratt 建议的那样简单地覆盖和扩展 UserManager 类中的方法。但这并不意味着你必须把婴儿和洗澡水一起扔掉。您仍然可以使用 Microsoft.AspNet.Identity 进行身份验证,只需编写自己的角色管理方法,将它们扩充为采用额外的参数:
AddToRoleAsync(TUser user, string roleName, string siteId);
public class Role : IRole<int>
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<UserInRole> Authorizes { get; set; }
}
public class UserInRole
{
public int RoleId { get; set; } // part of composite primary key
public int UserId { get; set; } // part of composite primary key
public string SiteId { get; set; } // part of composite primary key
public virtual Role Role { get; set; }
public virtual User User { get; set; }
}
public class User : IUser<int>
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<UserInRole> Authorized { get; set; }
}
鉴于上述情况,假设您的网址如下所示:
/sites/site-a/admin
/sites/site-b/manage
/sites/site-c/edit
/sites/{siteId}/do
...您可以编写一个自定义授权属性来检查 URL 并根据属性中的角色名称和 URL 中的 siteId 授权主体。要从属性访问 db,如果您将 IoC 用于 EntityFramework,则可以属性注入 DbContext 的实例(或任何包装它的接口)。