【问题标题】:ASP.NET Identity User and Roles in Multisite [closed]多站点中的 ASP.NET 身份用户和角色
【发布时间】:2014-02-05 14:30:41
【问题描述】:

我正在尝试在 ASP.NET MVC 应用程序中创建权限系统。我一直在学习最新的身份框架 - 以下是我的要求:

  1. 每组功能都有一组分层角色。因此,例如,可能有以下角色:
    • 继承
    • 读者
    • 编辑器
    • 经理
    • 管理员
  2. 每个用户在每个模块(例如事件、页面等)中都拥有其中一个角色
  3. 用户可以是安全组的成员。可以直接为每个安全组分配一个角色,然后该组中的所有用户(未明确分配该权限)将继承该角色。
  4. 多租户站点:每个用户都有一组他们所属的站点。在每个站点的上下文中,他们都有一套完整的权限,可以由站点管理员分配。

通过扩展 ASP.NET Identity,我是否有可能完成这一切?还是我应该从头开始构建一些自定义的东西?

【问题讨论】:

    标签: asp.net-mvc roles asp.net-identity


    【解决方案1】:

    10,000 次实施您自己的身份验证系统中有 9,999 次是错误的方法。任何事情都比这容易,而且要做到正确是一件看似困难的事情。 ASP.NET Identity 实际上是非常可定制的,因为它是专门为此目的而创建的。您可能需要做很多工作才能完全引导您的自定义需求,但使用 ASP.NET Identity 几乎肯定会更快、更安全。

    更新

    UserManager 的构造函数采用IUserStore 的实现。使用 Entity Framework 时,您通常只需提供 Microsoft.AspNet.Identity.EntityFramework.UserStore,但这是您的可扩展性的结合点。因此,您可以简单地将UserStore 子类化,然后覆盖GetRolesAsync 之类的东西来执行您需要实现的任何自定义角色逻辑。然后你就喂UserManager你的子类。

    【讨论】:

    • 我的直觉是同意 - 但默认情况下,角色只能由字符串标识 - 我将如何开始扩展它?我认为它们至少也需要通过 SiteID 来识别?
    • 所以在花了一些时间之后,这是我的想法 - 对于我所有的数据模型,我已经创建了存储库。所有存储库都有非常相似的逻辑,这意味着它们中的许多共享相同的基类。 UserManager/UserStore 的功能似乎与我的存储库对其他对象的功能相同,因此仅使用存储库而不是 UserManager 有一个例外不是更容易吗?
    • 另外 - 我使用反射器查看 UserManager 及其所有方法基本上只是包装了 UserStore 中存在的方法 - 这似乎是一个不必要的抽象层,除非我遗漏了什么?
    • UserStore 只是一个普通的存储库,具有所有基本的 CRUD 功能。 UserManager 更具有 auth-specific,用于设置密码、关联外部登录帐户等的逻辑。
    • 啊好吧 - 这有助于我更好地概念化这两个对象 - 非常感谢!
    【解决方案2】:

    在 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 的实例(或任何包装它的接口)。

    【讨论】:

    • 唯一的问题是我认为可能很难强制执行每种类型的角色只能有一个的事实(例如,站点 32 上的事件的单个“读取”角色)如果没有对使角色独一无二的对象(站点、事件模块和能力级别)的引用,那么我如何比较角色?
    • 如果您将您的角色命名为“事件阅读器”、“事件编辑器”、“事件管理器”等,您只需在其空格字符处拆分字符串即可获得模块和功能级别。这应该使角色足够独特。至于站点,真的是站点定义了角色吗?或者是用户在网站中担任该角色的授权 很重要?不太明白为什么需要以这种方式“比较角色”。
    • 但是,如果我将其称为“事件阅读器”,那么我将拥有重复的角色名称,因为我将为每个站点设置一个单独的角色名称 - 所以我可以将其称为“01 事件阅读器”,其中 01 是SiteID - 但这似乎有点老套,不是吗?
    • @William,我还是不明白为什么每个站点都必须有一组单独的角色。
    【解决方案3】:

    以下步骤可以解决您的问题

    1. 创建细粒度级别的角色...通常针对每个操作
    2. 将它们分组到 GroupRoles...以便管理员可以轻松管理它
    3. 向用户添加个人级别声明以获得特定权限

    下面是一些很好的例子

    希望这能解决你的问题

    【讨论】:

    • 第一个链接指向基于组的实现(在 CodeProject 中),它有一个好主意,但在我看来是一个糟糕的实现(它使用组,但在内部,它将用户添加到组,反之亦然)。第二个链接在 atm 断开。
    猜你喜欢
    • 1970-01-01
    • 2017-06-07
    • 1970-01-01
    • 1970-01-01
    • 2016-09-05
    • 2017-11-03
    • 1970-01-01
    • 2017-07-30
    • 2014-07-22
    相关资源
    最近更新 更多