一、注册流程(Register / Sign-up)

  1. 用户输入密码

    • 用户在注册页面输入密码,例如 "MyP@ssw0rd!"
  2. 后端生成哈希

    • 后端接收到明文密码后,调用 bcrypt:

      1
      2
      
      const bcrypt = require("bcryptjs");
      const hash = await bcrypt.hash(password, 12); // 12 是 cost
      
    • bcrypt 会自动:

      1. 生成随机盐(每个用户唯一)
      2. 把盐和密码一起进行加密运算
      3. 输出最终哈希(包含版本、成本因子、盐和密码哈希)
  3. 存储数据库

    • 数据库只存 哈希字符串,不存明文密码,也不需要单独存盐:

      1
      2
      3
      4
      
      {
        "username": "alice",
        "passwordHash": "$2b$12$CwTycUXWue0Thq9StjUM0u1Cq0sZ3L4y9yVb0z0s2cR4tH6B5mCq2"
      }
      
    • 这样即使数据库被泄露,攻击者也无法直接获得用户密码。


二、登录流程(Login / Sign-in)

  1. 用户提交凭证

    • 用户输入邮箱/用户名和密码(明文),例如 "MyP@ssw0rd!"
  2. 查找用户数据

    • 后端根据用户名或邮箱查询数据库,取出 passwordHash
  3. 验证密码

    • 调用 bcrypt.compare:

      1
      
      const ok = await bcrypt.compare(plainPassword, storedHash);
      
    • bcrypt 内部执行:

      1. 解析哈希中的盐、版本和 cost

        • 前 29 个字符包含算法版本、成本因子和盐
        • 例如 $2b$12$CwTycUXWue0Thq9StjUM0u
      2. 用同样的盐和 cost 对用户输入的明文密码重新计算哈希

      3. 比较计算结果与存储的哈希是否相同

  4. 判断结果

    • 如果相同 → 登录成功,创建会话 / JWT 等
    • 如果不同 → 登录失败,返回统一错误提示(不要暴露“用户不存在”或“密码错误”的差异)

三、bcrypt 的安全机制体现

特性 在注册/登录流程中体现
随机盐 每个用户的哈希都不一样,即使密码相同
可调成本 cost 控制哈希计算耗时,增加暴力破解难度
不可逆 数据库里的哈希无法还原密码
内嵌盐 登录时无需单独存储盐,compare 会自动解析
慢哈希 阻止攻击者批量暴力破解

四、流程图(文字版)

1
2
3
4
5
6
用户注册:
[用户输入密码] → [后端 bcrypt.hash(密码)] → [生成哈希,包含盐] → [存入数据库]

用户登录:
[用户输入密码] → [查数据库获取哈希] → [bcrypt.compare(明文, 哈希)]
      → [哈希解析盐和成本] → [重新哈希] → [比对是否一致] → [登录成功/失败]

五、工程实践小提示

  1. 异步接口bcrypt.hash / bcrypt.compare 使用异步,避免阻塞。
  2. 统一错误信息:防止信息泄露。
  3. 可静默升级成本:登录时检查成本,如果低于新目标 cost,可重新哈希更新数据库。
  4. 防暴力:加速限制、账户锁定、2FA。
  5. 密码长度注意:bcrypt 只处理前 72 字节,超长密码可先做 SHA256 压缩(可选,但要明确定义)。

✅ 总结: 从用户角度看,注册时密码被安全地加密存储登录时 bcrypt 会自动解析哈希中的盐并验证密码。 用户只需要输入密码,整个盐、成本和哈希的复杂处理都由后端和 bcrypt 库完成。


如果你愿意,我可以画一张 带盐解析细节的可视化流程图,让注册和登录的 bcrypt 哈希流程一目了然。

你想让我画吗?