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

3
我们有一个多租户ASP.NET应用程序。到目前为止,各个租户之间是相互隔离的,但现在我们有机构管理多个租户,并希望能够使用单个用户帐户管理所有租户。我正在尝试找出最佳方法来实现这一点,希望不需要对我们正在使用的现有技术进行太多更改。
相关技术细节:
- AspNetSqlMembershipProvider用于成员身份验证和角色 - C# 4.0(即将升级到4.5) - 表单验证 - aspx和MVC(v3)页面 - 假设有100个或更多租户,因此任何解决方案都需要支持该数量
我认为要求与SQL Server的安全模型非常相似。我们有一组登录名,代表可以登录系统的所有用户。用户应该能够被赋予一个或多个数据库(租户)的角色。例如:用户Bob在公司A拥有管理员角色,但在公司B只有用户角色。我们还有一个“sysadmin”角色,允许我们的公司员工访问任何租户以及特殊的管理权限,如创建/删除租户等。
我已经对各种库、框架等进行了大量研究,但没有发现任何令人信服的证据表明其他库或框架比我们当前拥有的更好。因此,我目前的想法是弄清楚如何让Sql Membership提供程序做我想要的事情,除非有人能指引我更好的方向。我也不确定我知道在这方面搜索最佳术语。
我考虑了两个选项:
1.只向成员身份验证提供程序添加少量角色,并在成员身份验证提供程序之外处理“当前用户是否在该租户具有此角色”的所有问题。成员身份验证提供程序将用于处理对系统的基本访问。
2.向成员身份验证提供程序添加特定于租户的角色。系统中将有(角色数)x(租户数)总角色。每个新租户都会向系统添加另一组角色,例如“Tenant A:Admin”,“Tenant A:User”等。需要一些额外的表来管理关系,以及可能需要一些自定义代码来确保从成员身份验证提供程序请求正确的特定于租户的角色。
以上两个选项哪个更好?或者我应该寻找其他支持?

1
嗨,Ted,我最近在这里回答了一个类似的问题,也许它会有用:https://dev59.com/fXvaa4cB1Zd3GeqPCmIX 简而言之,我认为选项#1将是最佳选择。 - danludwig
听起来将租户特定角色处理在会员提供者之外,让会员提供者执行粗粒度访问是正确的做法。 - Ted Elliott
2个回答

5

我认为你不可能将多租户功能嵌入任何现成的角色提供程序中,所以最好继续使用SqlMembershipProvider(和SqlRoleProvider)。即使是最新的Microsoft.AspNet.Identity仍然假定用户和角色之间是常规的多对多关系。你真正需要的是在这个多对多表的主键中添加第三列,用于标识你的租户,例如:

user: 6
role: 4
tenant: 17

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

通过这种方式,您可以为不同的租户拥有具有不同特权的用户,所有用户都使用相同的角色名称。

如果选择第二种选项,则您的[Authorize]属性将会变得非常繁琐。 想象一下:

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

如果不使用自定义的AuthorizeAttribute,那么所有这些属性都必须在编译时写入。但即使如此,每次向系统添加租户时仍需要创建一组新角色,这是不必要的。


我正在使用您的第一种方法,并在我的ApplicationUserRole中添加了一个名为'companyId'的属性,但是我卡在了重写AddToRole或IsInRole这些函数上。这些函数不接受companyId。我正在尝试重写这些函数以将companyId纳入考虑。我已经在这里发布了我的问题。如果您能帮忙,那就太好了!谢谢 - ChengWhyNot

4
我在开发一个大型的多租户应用程序。我们得出结论,为每个租户维护单独的数据库并使Web应用程序自动切换数据库上下文比尝试使用过于复杂的数据库模式来建模不同的租户更容易维护。 优点
  1. 默认将租户数据分隔到不同的数据库中
  2. 可以导出租户数据作为客户MI的数据库转储
  3. 大大简化了数据库设计
缺点
  1. 必须管理多个数据库-操作具有挑战性
  2. 必须开发数据库切换代码
使用多个数据库的实现
  1. 我们使用配置数据库,该数据库具有基于帐户代码的客户端设置。该帐户代码可以来自登录屏幕,或者您可以将子域映射到客户端代码。
  2. 当应用程序启动时,您将所有租户加载到缓存中(包含连接字符串)
  3. 在每个请求上,您必须确定客户端,然后切换db上下文
我还开发了一个使用单个数据库的多租户应用程序。您很快就会遇到确保不跨租户数据的问题。每个查询都需要包括租户ID过滤器。因此,数据库查询总是变慢,尽管您可以索引所有内容以尝试改善情况。
关于成员资格问题,您可以将成员资格架构安装到每个租户数据库中。
什么不起作用
理想的替代方案是动态切换ApplicationName,但尽管它似乎有效,ApplicationName不是线程安全的,因此这不是可靠的:
由于同一个 HttpApplication 对象为所有请求使用单个默认成员资格提供程序实例,因此您可以同时有多个请求执行并尝试设置 ApplicationName 属性值。ApplicationName 属性对于多个写入不是线程安全的,并且更改 ApplicationName 属性值可能会导致应用程序的多个用户出现意外行为。我们建议您避免编写允许用户设置 ApplicationName 属性的代码,除非必须这样做。需要设置 ApplicationName 属性的应用程序示例是管理多个应用程序的成员身份数据的管理应用程序。此类应用程序应该是单用户应用程序而不是 Web 应用程序。
备选方案:MembershipReboot
在.Net中,多租户很难处理。使用内置成员资格的开源替代方案是使用MembershipReboot,它由Brock Allen编写。它具有一些出色的功能,包括开箱即用的多租户支持。
  1. 单租户或多租户账户管理
  2. 灵活的账户存储设计(关系型/SQL或对象/NoSql),使用EF和RavenDB两种方式进行示例
  3. 支持声明感知用户身份
  4. 支持账户注册、邮件验证、密码重置等功能
  5. 支持基于多次失败登录尝试(密码猜测)的账户锁定
  6. 可扩展的电子邮件通知模板
  7. 可自定义的用户名、密码和电子邮件验证
  8. 账户活动和更新的通知系统(例如用于审计)
  9. 与外部身份提供商(企业或社交)链接的账户链接
  10. 支持基于证书的身份验证
  11. 正确的密码存储(通过PBKDF2)
  12. 可配置的迭代次数
  13. 默认采用OWASP对于迭代的建议(例如在2012年为64K)
  14. 通过手机短信或客户端证书支持双因素身份验证

最常见的用例是将其集成到ASP.NET或ASP.NET MVC应用程序中,但该库也可以作为服务通过网络使用。

另一选择:ServiceStack REST

如果您正在构建大量使用JavaScript MVC框架(如AngularJS,EmberJS或BackboneJS)的现代Web应用程序,则另一种选择是使用ServiceStack REST服务。 ServiceStack具有长列表的身份验证功能,从我对SS的经验来看,我发现它具有非常深思熟虑的API模型。

ServiceStack Authentication


1
这是否意味着OP的用户Bob需要创建两个用户帐户,一个用于公司A,另一个用于公司B? - danludwig
正确。但是为什么公司A的用户A要访问公司B的信息呢? - Rebecca
根据 OP 的要求,似乎需要这样一个功能:“我们有一些机构管理多个租户,并希望能够使用单个用户帐户来管理所有租户。” 请别误解,我认为在某些多租户情况下,这是一个好的解决方案。 - danludwig
我们为每个租户设置了单独的数据库,并有一个配置数据库。会员数据存储在配置数据库中。 - Ted Elliott
@TedElliott 我们也使用单个配置数据库,但它只确定租户数据库的连接字符串。如果您将租户用户保存在配置数据库中,那么您如何对其进行分隔? - Rebecca

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接