# 官方扩展库 `bcrypt` ## `bcrypt` 是什么 `bcrypt` 是专门用于**密码哈希**的算法,不是可逆加密。 ```text password ↓ bcrypt ↓ hash ``` ### 核心原则 ```text 数据库只存 hash bcrypt 自动处理 salt 使用 CompareHashAndPassword 验证 cost 根据服务器性能决定 不要自己用 MD5 / SHA256 存密码 ``` ------ ### cost `cost` 是 bcrypt 的计算难度。 生产环境通常可以从 `10 ~ 12` 开始测试,具体值应根据服务器性能决定。单次 bcrypt 计算控制在大约 `100ms ~ 300ms` 是比较常见的工程范围。 `cost` 每增加 1,计算量大约接近翻倍。 cost 越高: - 计算越慢 - 暴力破解成本越高 - 服务器 CPU 消耗也越高 bcrypt 生成的 hash 本身会记录当时使用的 `cost`。 因此,即使生产环境将当前配置从: ```text cost = 10 ``` 升级为: ```text cost = 12 ``` 数据库中原本使用 `cost = 10` 生成的旧 hash,仍然可以通过: ```go bcrypt.CompareHashAndPassword(...) ``` 正常验证。 `CompareHashAndPassword` 会根据已有 hash 中记录的信息进行密码验证,不依赖当前系统配置的 cost。 登录成功后,可以通过 `bcrypt.Cost()` 获取旧 hash 使用的 cost。如果低于当前配置,可以重新生成 hash 并更新数据库。 ------ ### 72-byte 限制 bcrypt 密码输入最多支持约 `72 bytes`。 如果只允许 ASCII: ```text 72 bytes ≈ 72 个字符 ``` 通常已经足够。 建议业务层主动限制: ```go if len([]byte(password)) > 72 { return errors.New("password too long") } ``` ------ ## 基础使用 ### 依赖导入 ```go import "golang.org/x/crypto/bcrypt" ``` `golang.org/x/crypto/bcrypt` 属于 Go 官方维护的扩展库,但不属于 Go 标准库。 ------ ### 生成 hash ```go hash, err := bcrypt.GenerateFromPassword( []byte(password), 12, ) ``` 生成的 hash 类似: ```text $2a$12$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` 其中大致包含: ```text $2a$ → bcrypt 版本 12$ → cost 后续内容 → 编码后的 salt + hash ``` bcrypt 会自动生成随机 salt,因此不需要自己生成或额外保存 salt。 ------ ### 验证密码 ```go err := bcrypt.CompareHashAndPassword( []byte(passwordHash), []byte(password), ) if err == nil { // 密码正确 } ``` ------ ### 判断密码是否相同 不能直接比较两个 bcrypt hash,因为 bcrypt 每次都会生成新的随机 salt。 即使密码完全相同: ```text hash1 != hash2 ``` 判断某个密码是否和旧密码相同时,应使用: ```go err := bcrypt.CompareHashAndPassword( []byte(oldHash), []byte(password), ) if err == nil { // password 与 oldHash 对应的密码相同 } ``` ------ ### 获取 hash 的 cost ```go cost, err := bcrypt.Cost( []byte(user.PasswordHash), ) ``` 通常用于**用户登录成功后**,判断当前密码 hash 是否使用了旧的 cost。 例如: ```text 数据库 hash cost = 10 当前系统 cost = 12 ↓ 登录成功 ↓ 重新生成 cost = 12 的 hash ↓ 更新数据库 ``` 这样可以在用户正常登录过程中逐步升级旧密码 hash。