← All posts

无需重置密码即可导入用户

Authagonal·June 21, 2026
migrationpasswordsbcrypt

每一份身份迁移指南最终都会写到同一段话,而且口气总带着几分歉意:"用户需要重置密码。"这被当成了自然法则,其实并非如此。它是一种选择,往往是被某个不愿做更难那件事的工具逼出来的。

更难的那件事,是就地验证用户现有的密码哈希,让他们在迁移之后用和此前完全相同的凭据登录,丝毫察觉不到发生过什么。能不能做到,归结为一个问题:你能拿到旧哈希吗,新系统又能验证它们吗?

密码哈希比人们想的更易于迁移

密码哈希不是什么秘密算法。bcrypt 就是 bcrypt。一个 bcrypt 哈希把自己的代价因子和盐都装在字符串里,所以任何实现了 bcrypt 的系统都能验证由其他任何 bcrypt 系统生成的哈希。ASP.NET Identity 所用的 PBKDF2 格式也是一样:有文档、有版本、自描述。只要你知道自己手里拿的是什么,就能拿一个密码去核对它,而完全不必知道那个密码本身。

因此,一次保留登录的迁移既不需要明文(谁也没有),也不需要预先把所有人重新哈希一遍。它要做的,是取得已存储的哈希,并在登录时拿来验证,在用户首次登录时悄悄把每一个都升级到它自己的格式。最后这一步就是惰性迁移:把旧哈希带过来,验证一次,再透明地替换掉。在几周的正常登录里,你的用户表会自行重新哈希,旧格式逐渐淡出,没有一次重置,也没有一张工单。

双路径这一点

棘手之处在于,不同来源交给你的是不同格式,而一个好的导入器对两者都会验证:

  • 来自自托管的 Duende / ASP.NET Identity: V3 的 PBKDF2 哈希(以及任何遗留的 bcrypt)都能原生验证,并在首次登录时重新哈希。这是最容易的情形,因为它正是目标端本就在用的方案。大多数团队都没想到会这么干净利落。
  • 来自 Auth0: bcrypt 哈希能逐字验证。难点不在格式,而在于把它们拿到手

真正做不到的时候

Auth0 的 Management API 从不返回密码哈希。这是一项刻意的策略,并非任何人工具上的缺口;凡是号称无需它就能"一键导出 Auth0"并附带密码的说法,你都应当心存怀疑。受支持的途径是一次由支持团队协助的批量导出:一个 NDJSON 文件,内含每位用户的 bcrypt 哈希。拿到这个文件,哈希就能逐字导入,惰性迁移一并到位,整次迁移依旧悄无声息。

如果等不了,退而求其次的办法,就是那段歉意话语的诚实版本:用户在首次登录时设置一个新密码。什么也没丢,因为本来就没有哈希可带。区别在于,这是你心知肚明地选择了它,而不是因为工具做不到更好。

为什么这件事比听起来更要紧

在迁移途中,强制重置是你能对用户群做的最显眼、最令人不安的一件事。它带来支持负担,它把用户训练成会去期待一封形似钓鱼的"点此重置"邮件,而那一刻,一次悄无声息的基础设施变更就成了所有人的麻烦。避开它,正是让一次迁移感觉上像什么都没发生过的关键所在,而迁移本就该有这种感觉。

所以,在接受"所有人都重置"之前,先问这两个问题。对大多数迁移而言,两个答案都是肯定的,那段歉意的话从来就没有必要。

看看一次导入会带过来什么:预览是只读的,在写入任何内容之前就把一切呈现给你。