具有租户特定角色的ASP.NET多租户应用程序

时间:2014-01-20 23:57:00

标签: c# asp.net asp.net-mvc-3 asp.net-membership roleprovider

我们有一个多租户ASP.NET应用程序。到目前为止,租户已经相互隔离,但现在我们有代理商管理多个租户,并希望能够通过一个用户帐户管理所有租户。我正在努力找出实现这一目标的最佳方法,希望对我们正在使用的现有技术没有太大的改变。

相关技术细节:

  • 会员和角色的AspNetSqlMembershipProvider
  • C#4.0(即将成为4.5)
  • 表单身份验证
  • aspx和MVC(v3)页面
  • 假设有100个或更多租户,因此任何解决方案都需要支持

我认为这些要求与SQL Server的安全模型非常相似。我们有一组登录,代表可以登录系统的所有用户。用户应该能够被赋予一个或多个数据库(租户)的角色。示例:用户Bob在公司A中具有管理员角色,但在公司B中只有用户角色。我们公司的员工也有“sysadmin”角色,允许我们访问任何租户以及专门的管理权限,例如创建/删除租户等等。

我已经对各种库,框架等进行了大量研究,但我没有找到任何令人信服的证据证明其他库或框架会比我们现有的更好。所以我现在正在考虑如何让Sql Membership provider做我想做的事情,除非有人能指出我更好的方向。我也不确定我知道在这里搜索的最佳术语。

我正在考虑两个选项:

  1. 仅向会员提供商添加少量角色,并处理会员提供商之外的“当前用户在此租户中是否具有此角色”的所有问题。会员提供者将用于处理对系统的基本访问。
  2. 将租户特定角色添加到成员资格提供程序。我们会在系统中拥有(角色数)x(租户数量)的总角色。每个新租户都会向系统添加另一组角色,例如“租户A:管理员”,“租户A:用户”等。需要一些额外的表来管理关系以及可能需要一些自定义代码,以确保访问权限从成员资格提供者请求正确的租户特定角色。 / LI>

    这些选项中的任何一个都不错吗?或者我应该在别处寻找对此的支持?

2 个答案:

答案 0 :(得分:4)

我认为您不会将多租户强加到任何开箱即用的角色提供程序中,因此您可以继续使用SqlMembershipProvider(和SqlRoleProvider)。即使是最新的Microsoft.AspNet.Identity仍然假定用户和角色之间存在多对多的普遍现象。您真正需要的是在该多对多表的主键中添加第3列,该表将标记您的租户,即:

user: 6
role: 4
tenant: 17

user: 6
role: 9
tenant: 18 (and so on)

...通过这种方式,您可以让用户拥有不同租约的不同权限,所有这些都使用相同的角色名称集。

如果您使用选项#2,那么您的[授权]属性将会爆炸。想象一下:

[Authorize(Roles = "TenantA:Admin", "TenantB:Admin", ...)]
public ActionResult Post(int id, SomeViewModel model) {}

...所有这些属性都必须在编译时写入,除非你使用自定义的AuthorizeAttribute,你可以这样做。但即便如此,每次向系统添加租户时,您都会创建一组新的角色,这不是必需的。

答案 1 :(得分:3)

我在一个大型多租户应用程序上工作。我们得出的结论是,每个租户更容易维护单独的数据库,让Web应用程序自动切换数据库上下文,而不是尝试使用过于复杂的数据库模式来为不同的租户建模。

好处

  1. 租户数据默认划分为不同的数据库
  2. 租户数据可以导出为客户端MI的数据库转储
  3. 数据库设计大大简化
  4. 缺点

    1. 您必须管理多个数据库 - 操作挑战
    2. 您必须开发数据库切换代码
    3. 使用多个数据库实施

      1. 我们使用的配置数据库具有基于帐户代码的客户端设置。该帐户代码可以来自登录屏幕,也可以将子域映射到客户端代码。
      2. 当应用程序启动时,您将所有租户加载到缓存(包含连接字符串)
      3. 在每个请求中,您必须确定客户端,然后切换数据库上下文
      4. 我还开发了一个使用单个数据库的多租户应用程序。您很快就会遇到问题,确保您不会跨租户数据。每个查询都需要包含租户ID过滤器。因此,数据库查询总是较慢,尽管您可以索引所有可以尝试的内容并改善情况。

        关于成员资格问题,您可以将成员资格架构安装到每个租户数据库中。

        什么行不通

        理想的替代方案是dynamically switch the ApplicationName,但虽然它似乎有效,ApplicationName is not thread safe,但这不可靠:

          

        因为所有人都使用单个默认成员资格提供程序实例   您可以使用HttpApplication对象提供的请求   多个请求并发执行并尝试设置   ApplicationName属性值。 ApplicationName属性不是   线程可以安全地进行多次写入,并更改ApplicationName   属性值可能导致多个用户的意外行为   一个应用程序。我们建议您避免编写允许的代码   用户设置ApplicationName属性,除非必须。一个例子   可以是设置ApplicationName属性的应用程序   required是管理成员资格数据的管理应用程序   适用于多种应用。这样的应用程序应该是单用户   应用程序,而不是Web应用程序。

        替代方案:MembershipReboot

        .Net中的多租户很难。使用内置成员身份的开源替代方法是使用由MembershipReboot编写的Brock Allen。它具有一些出色的功能,包括开箱即用的多租户支持:

          
            
        1. 单租户或多租户帐户管理
        2.   
        3. 灵活的帐户存储设计(关系/ SQL或对象/ NoSql),使用EF和RavenDB的示例
        4.   
        5. 声明感知用户身份
        6.   
        7. 支持帐户注册,电子邮件验证,密码重置等。
        8.   
        9. 多次登录尝试失败的帐户锁定(密码猜测)
        10.   
        11. 电子邮件通知的可扩展模板
        12.   
        13. 可自定义的用户名,密码和电子邮件验证
        14.   
        15. 帐户活动和更新通知系统(例如审核)
        16.   
        17. 帐户与外部身份提供商(企业或社交)的链接
        18.   
        19. 支持基于证书的身份验证
        20.   
        21. 正确密码存储(通过PBKDF2)
        22.   
        23. 可配置的迭代
        24.   
        25. 默认为OWASP迭代建议(例如2012年为64K)
        26.   
        27. 通过手机短信或客户端证书支持双因素身份验证
        28.         

          最常见的用例是将其集成到ASP.NET或   ASP.NET MVC应用程序,虽然库也可以使用   网络即服务。

        替代方案:ServiceStack REST

        如果要构建大量使用JavaScript MVC框架(如AngularJS,EmberJS或BackboneJS)的现代Web应用程序,则另一种方法是使用ServiceStack REST服务。 ServiceStack有一个long list of Authentication features,根据我的SS经验,我发现它有一个经过深思熟虑的API模型。

        ServiceStack Authentication