如何将现有用户数据库迁移到 Logto 的通用指南
本文介绍了如何利用现有工具,将之前的用户数据迁移到 Logto,尤其是在 Logto 尚未提供数据迁移服务的情况下。
Logto 目前还没有一套数据迁移的工具,但我们已经开放了 Management API 的基本功能。这不会阻碍用户通过编写脚本完成现有用户数据库的迁移。
鉴于我们从社区用户那里收到的一些需求,以及我们目前还没有文档解释用户数据库迁移的具体步骤,我们在这篇文章中适当地做了介绍,帮助用户找到具体的思路,节省查阅 Logto 代码和文档的时间。
第 1 步:理解 Logto 的基本用户数据结构和使用场景
Logto 使用的是 PostgreSQL 数据库。除了各种性能优势外,一个重要的原因是它支持自定义 JSON / JSONB 数据类型,并允许在 JSON 类型数据的内部值上建立索引,平衡数据库性能和可扩展性。
关于 Logto 的用户数据结构,请参考 用户参考 以了解所有详细信息。这里我们重点描述一些 Logto 可能与其他身份服务不同的方面。
id
这是 Logto 为用户随机生成的内部唯一标识符。用户在使用基于 Logto 的服务时,不知道 id
。
熟悉数据库的工程师应该不会觉得这奇怪。就算是最基础的身份系统也会有一个 id
来唯一标识用户,尽管 their 形式通常有所不同。一些身份服务可能会用用户名来唯一标识用户。
username, primaryEmail, primaryPhone
在这里,用户名、主要电子邮件、主要电话,是 Logto 与其他身份系统大大不同的地方——它们都可以作为终端用户可以 perceptible 的唯一标识符。
在许多其他的身份系统中,用户名被用作识别(用户名不能在帐户之间重复),这很容易被理解。
但在 Logto 中,主要的电子邮件/电话也被用来区分用户。也就是说,如果一个用户 A 已经有了主要的电子邮件 [email protected],那么其他的用户 B 就不能添加这个电子邮件地址作为他们的主要电子邮件。主要电话同样适用。
一些其他的身份系统允许使用不同的用户名注册多个帐户,但绑定相同的电子邮件/电话,这在 Logto 中是不允许的(电子邮件/电话可以添加到 Logto 的 customData
)。这是因为 Logto 中的主要电子邮件/电话可以用于无密码签名。
identities
Logto 把这个 identities
字段定义为 JSON 类型,其类型定义:
近年来,为了便于获取新用户,身份系统允许用户通过一些现有的大用户群社交账户快速登录,如 google
/ facebook
等。
在下面的例子中,identities
字段存储了社交登录信息:
在这里,facebook
和 github
是社交提供者的名字,userId
是用户的社交账号的 id
,用于登录。details
也包括一些其他用户已经授权社交提供者显示的信息,这些信息会在特定的时间添加到用户的 Logto 用户配置文件中。
如果之前的数据库包含用户使用的社交供应商的名称(比如 facebook
, google
)和 id
(看前面的例子中的 userId
),那么 Logto 用户可以直接用相同的社交账户登录。
customData
这个字段可以存储任何与用户相关的信息,比如上面提到的不能用于无密码登录的电子邮件/电话(可能用于接收通知或用于其他业务相关的功能),等等。
其他字段相对较易理解(排除 passwordEncrypted
和 passwordEncryptionMethod
,稍后将解释),请自行阅读文档。
第 2 步:编写数据库迁移脚本
对于大规模的数据库迁移,编写迁移脚本是最常见的方法。我们提供一个简单的例子,帮助理解如何编写迁移脚本来满足不同的需求。
需要注意的是,编写迁移脚本时,我们跳过了提取原始数据的过程,因为获取数据的方式有很多种,比如从数据库导出到文件然后读取文件,或者通过 API 提取。这些都不是迁移脚本的重点,所以我们在此不详细讨论。
当你在迁移脚本中看到 tenant_id
,你可能会感到奇怪。Logto 是基于多租户架构的。对于开源 Logto(Logto OSS)用户,你可以直接把用户的 tenant_id
设置为 default
。
对于自托管的 Logto OSS 用户,获取数据库连接很容易。然而,对于 Logto 云用户,由于安全原因,我们目前不能向用户提供数据库连接权限。用户需要参考 API 文档 并使用用户相关的 APIs 进行用户迁移。我们了解到这种方法不适合大规模的用户数据迁移,但在这个阶段仍然可以处理有限数量的用户迁移。
第 3 步:哈希密码迁移挑战和潜在的解决方法
在我们之前的 博客 中,我们谈到了一些防止密码攻击的措施。身份基础设施提供商能做的一件事是不存储明文密码,而是保存哈希密码。
另一篇 博客文章 解释了密码哈希,其中提到哈希值是不可逆转的。
第二篇博文还比较了一些哈希算法的演变。Logto 本身使用的是文章中提到的 Argon2i 算法,并且目前不支持其他哈希算法。这意味着旧用户数据库使用其他哈希算法的密码哈希不能直接迁移到 Logto 的数据库。
即使 Logto 在 Argon2i 之外支持其他常用的哈希算法,由于使用哈希算法时 salt 的灵活性,也很难直接迁移旧数据。
除了将来支持其他的哈希算法外,Logto 也可能提供自定义 salt 计算方法来适应各种情况。
在此之前,你可以使用 Logto 的 sign-in experience configuration 来允许用户通过其他方式登录(如电子邮件 + 验证码),并在进入应用程序之前填写一个新密码(将使用 Argon2i 哈希算法)。然后可以使用新密码登录。
需要注意的是,如果原始用户数据只支持密码登录,那么上述的解决方法将不适用于这种场景。这是因为上述的解决方案实际上是通过使用其他登录方式并利用 Logto 的终端用户流程中的 "required information completion" 机制来解决密码哈希不兼容的问题。
所以,如果原始用户数据只支持密码登录,那么这个解决方案无法解决这种情况,因为没有可用的其他登录选项。
这里提到的解决方法并没有真正解决哈希密码迁移问题,而是从 Logto 产品的角度提供了一个替代解决方案,以避免阻止用户登录你的产品。
第 4 步:逐步切换到 Logto 并监控状态
完成上述步骤后,终端用户已经可以通过 Logto 登录并使用你的服务了。
由于在迁移过程中通常不会中断服务,因此可能会有用户数据同步到 Logto 的数据不是最新的情况。当检测到这种不常见的情况时,需要从旧数据库到 Logto 进行同步。
在较长的时间后(或者可能应用了其他定义的度量)没有出现不一致的数据,就可以完全废弃旧数据库。
结论
在文章中,我们介绍了理想的数据库迁移应走过的 步骤。
如果你遇到了上面没有提到的问题,不要犹豫加入我们的社区或联系我们寻求帮助。你遇到的问题也可能被其他人遇到,并将成为我们在设计未来的迁移工具时需要考虑的问题。