如何使用Apartment将现有的Rails应用程序迁移到多租户应用程序?

时间:2019-01-28 20:39:22

标签: ruby-on-rails devise multi-tenant apartment-gem

我正在努力通过ApartmentDevise将现有的单租户应用程序迁移到多租户应用程序,而无需使用子域。

我已经有一个拥有Company的应用程序,并且该用户属于该公司的多个用户。
现在,我需要扩展该应用程序并添加另一个Company。每个公司的用户都是唯一的,并通过电子邮件进行标识。

所以我想要的是-当用户尝试登录时-我想根据用户的电子邮件切换为正确的租户,然后继续进行操作。

我想将所有User数据保存在单独的数据库中。在这里,我不明白如何查找正确的租户?所有指南和示例都说明了如何将User添加到excluded_models中。但这对我不起作用。

看来,public表所在的地方应该有一些UserEmail_Tenant模式吗?还是我错过了什么?

1 个答案:

答案 0 :(得分:1)

您实际上只有2个选择:

  1. 确保用户通过租户特定的URL登录
  2. 有一种将登录凭据映射到租户的方法

(2)将登录凭据映射到租户的问题在于,它禁止凭据的重用,这在用户希望同时访问您的2个租户时是一个显示停止器。要处理这种情况,您必须给每个用户一个全局唯一的特定于租户的ID。您可以做到

  • 通过包括在用户ID某种租户标识符的(丑陋)或
  • 通过强制具有多个租户的帐户的用户使用每个租户都不同的某些全局唯一ID登录(糟糕的用户体验,客户支持团队难以解释)。

大多数企业发现这些选项不可接受的,但我已经看到它偶尔做。

在拥有多个租户的帐户的情况下,大多数公司更愿意允许用户重复使用其电子邮件地址作为其ID。两家公司选择给每个租户一个唯一的登录URL来隔离租户帐户,并且不允许通过其(具有多租户应用程序的服务提供商公司)主站点直接登录。相反,他们要求用户转到租户的站点并单击那里的某种login按钮,将他们定向到租户特定的登录页面。

特定于租户的URL的选项包括:

  • 要求登录URL在URL中具有tenant_id参数。例如https://example.com/login?tenant_id=tenant.com
  • 要求登录URL位于特定于租户的子域上。

大多数公司选择子域的原因有两个:

  1. 与在URL中放置租户ID参数相比,它使URL更为整洁。
  2. 这意味着每个租户都可以具有完全相同的路径,并且每个URL都没有租户参数,这可以使开发和测试更加容易,并且SEO更加有效。
  3. 在页面内容因租户而异的情况下,它更好地支持搜索引擎和SEO,因为页面变体位于不同的域中,不会折叠到一个域中并由一些不透明的参数触发。
  4. 这使分片更加容易。当你达到赴汤蹈火合理的数值,一台服务器上的租户,而不是跳跃继续垂直扩展,你可以很容易地通过水平借力子域将流量路由到不同的服务器(集群)下一组租户规模。

因此,尽管您可以创建到租户的电子邮件登录图,但我建议您这样做,因为一旦用户尝试使用同一电子邮件与第二个租户创建帐户,该登录就会痛苦地失败。即使你认为没有人会做到这一点,你不能确定,尤其是公司不断发展壮大。即使99.99%的用户不这样做,到100万用户时,您中也会有100个这样做。

如果您承诺允许用户使用其电子邮件地址作为其用户ID并通过公共URL登录,则建议您通过文本框或选项使您的用户也指示他们正在登录的租户。菜单,您可以根据Cookie值进行预填充/选择。在这种情况下,要登录,您必须提供用户ID,租户ID和密码。

如果您同意用户不使用其电子邮件地址作为用户ID,那么我将使租户ID成为用户ID的一部分。所以,用户在租户“史蒂夫“容易”可能有用户ID“steve_apt”。

这2个选项既允许用户在多个租户上拥有帐户,又可以在没有用户ID到租户的任何全局映射的情况下工作。

如果您致力于允许用户使用其电子邮件地址作为其用户ID并通过通用URL登录,并且您不希望他们必须知道该公司是其租户的事实,许多您的系统上,那么,你需要某种类型的用户ID的租户映射。

您可以通过多种方式提供这种映射。

  • 您可以在浏览器中放置一个cookie,以提示您该用户属于哪个租户,然后仅查询该租户。如果该查询失败或未提供cookie,则可以退回轮询所有租户。
  • 您可以通过一些后台作业或按需补Redis的缓存,用户ID和租户之间的映射,以及推送更新时添加新用户,这样就可以在Redis的缓存做一个快速查找几乎所有的时间,再次回落到轮询一次租户只有在高速缓存未命中的罕见病例。
  • 您可以将登录视为第三方提供商。创建一个仅存储用户ID,密码和租户的应用程序,让人们登录该应用程序,然后该应用程序将登录的用户转发给主应用程序,并传递经过身份验证的用户ID和租户ID。如果所有应用程序都在同一个域中,那么就不难了,因为所有身份验证应用程序需要做的是创建相同的cookie,其他应用程序已经接受了该cookie来对登录用户进行身份验证。

也可以通过几种不同的方式来轮询租户以查找用户ID。我不推荐这种方法一般,但如果你要做到这一点,我会建议你做使用PostgreSQL数据库,保持每个租户在一个单独的模式的内相同的数据库,并使用物化视图或PL / pgSQL存储的动态SQL过程。

我不是所有这些解决方案的忠实拥护者,但是我想我是否必须选择使用authentication模式,该模式包含用户ID,密码和tenant_id,仅用于登录,最好使用隔离的,更安全的应用。你如何处理,这需要在2个不同地方的用户ID的租户映射存在既可以奠定声称是真理的来源,当他们不同意你这样做,什么是它我的主要建议是有问题的事实首先不要这样做。