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

Mar*_*kus 3 ruby-on-rails multi-tenant devise apartment-gem

我正在努力将现有的单租户应用程序迁移到使用ApartmentDevise不使用子域的多租户应用程序。

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

所以我想要的是 - 当用户尝试登录时 - 我想根据用户电子邮件切换到正确的租户并继续流程。

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

所以看起来,表public所在的地方应该有一些模式UserEmail_Tenant?或者我错过了什么?

Old*_*Pro 5

你真的只有两个选择:

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

(2) 将登录凭据映射到租户的问题在于它禁止凭据重用,当用户想要同时访问您的 2 个租户时,这是一个阻碍。要处理这种情况,您必须为每个用户提供一个全局唯一的租户特定 ID。你能行的

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

大多数公司认为这些选择是不可接受的,但我有时也看到这样做了。

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

租户特定 URL 的选项包括:

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

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

  1. 它比在 URL 中放置租户 ID 参数更简洁。
  2. 这意味着每个租户可以拥有完全相同的路径,并且每个 URL 没有租户参数,这可以使开发和测试更容易,SEO 更有效。
  3. 在页面内容因租户而异的情况下,它更好地支持搜索引擎和 SEO,因为页面变体位于不同的域中,而不是折叠到一个域中并由某些不透明参数触发。
  4. 它使分片更容易。当您在一台服务器上达到一定数量的租户时,您可以轻松地通过利用子域将流量路由到不同服务器(集群)以供下一组租户继续垂直扩展,而不是跳过箍环继续垂直扩展。

因此,虽然您可以创建租户的电子邮件登录地图,但我建议不要这样做,因为一旦用户尝试使用同一电子邮件与第二个租户创建帐户,它就会失败。即使您认为没有人会这样做,您也无法确定,尤其是随着您公司的发展。即使你 99.99% 的用户没有这样做,当你达到 100 万用户时,你也会有 100 名用户这样做。

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

如果您同意用户不使用他们的电子邮件地址作为用户 ID,那么我只会让租户 ID 成为用户 ID 的一部分。因此,租户 'apt' 中的用户 'steve' 可能具有用户 ID 'steve_apt'。

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

如果您承诺允许用户使用他们的电子邮件地址作为他们的用户 ID 并让他们通过一个公共 URL 登录,并且您不希望他们知道该公司是您的许多租户中的一个系统,那么是的,您需要某种用户 ID 到租户映射。

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

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

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

我不是这些解决方案中的任何一个的忠实粉丝,但我想如果我必须选择,我会使用一个authentication包含用户 ID、密码和租户 ID的架构,并且仅用于登录,最好是由一个孤立的和更安全的应用程序。您如何处理这需要用户 ID 到租户映射存在于 2 个不同地方的事实,这两个地方都可以声称是事实的来源,以及当他们不同意时您会怎么做,这是我的主要建议首先不要这样做。