bcrypt.comparehashandpassword第一个参数必须是哈希值、第二个是原始密码,顺序颠倒必然失败;哈希值每次不同是因随机salt,比对须用该函数而非直接比较;登录应统一返回“invalid credentials”防信息泄露。

bcrypt.CompareHashAndPassword 顺序反了就永远失败
这是 Go 项目里最常踩的坑:把数据库查出来的哈希值当第二个参数、用户密码当第一个,结果 bcrypt.CompareHashAndPassword 每次都返回 crypto/bcrypt: hashedPassword is not the hash of the given password,但你根本没意识到是参数位置错了。
-
CompareHashAndPassword第一个参数必须是哈希值([]byte),第二个才是原始密码([]byte) - 哈希值通常从 DB 查出来是字符串,别忘了用
[]byte(hashStr)转换;用户密码也得转[]byte,不能直接传 string - 如果哈希值开头是
$2a$、$2b$或$2y$,说明它确实是 bcrypt 格式,可直接喂给这个函数——不用解码、不用截断、不手动验盐
为什么每次 bcrypt.GenerateFromPassword 结果都不同?这反而是对的
你看到数据库里存的哈希值每次都不一样,哪怕输同一个密码,就怀疑“是不是加密错了”——其实完全正常。bcrypt 每次生成哈希时都会自动加一个随机 salt,并把 salt 和 hash 一起编码进最终字符串(比如 $2a$10$abc123...)。所以比对时绝不能自己再算一次哈希去 ==,而必须用 CompareHashAndPassword。
- 它内部会从哈希字符串里自动提取 salt,再用同样的 cost 和 salt 对输入密码重新哈希,然后恒定时间比对
- 这也意味着:你永远不该在代码里写
user.Password == input.Password或hash1 == hash2这类逻辑 - 测试时如果想复现相同哈希(仅限单元测试),得用固定 salt + 固定 cost,但生产环境必须让 bcrypt 自己随机生成
登录接口返回 {"error": "invalid credentials"} 不是偷懒,是防信息泄露
如果登录失败时返回 {"error": "user not found"} 或 {"error": "wrong password"},等于告诉攻击者哪个用户名存在——暴力破解立刻从“猜账号+密码”降维成“只爆破密码”。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 统一返回
{"error": "invalid credentials"},前端无法区分是账号错还是密码错 - 后端日志里也禁止打
log.Printf("login failed for %s with pwd: %s", user, pwd)——明文密码一旦进日志,等于裸奔 - 正确做法是:只记
log.Printf("login failed for user %s from %s", username, r.RemoteAddr),且 IP 维度做限流(比如 5 分钟内最多 6 次)
别跳过数据库查询的 ErrNoRows,否则空指针或静默失败
用 GORM 或 database/sql 查用户时,如果用户名不存在,db.Where("username = ?", u).First(&user) 会返回 gorm.ErrRecordNotFound(或 sql.ErrNoRows),而不是 nil。很多同学直接忽略 err,接着调 bcrypt.CompareHashAndPassword(user.Password, inputPwd),结果 user.Password 是空字符串或零值,比对必然失败,还可能 panic。
- 必须显式检查错误:
if errors.Is(err, sql.ErrNoRows) { return false, nil } - 更安全的做法是先查是否存在,再比密码;或者查完直接用
if user.ID == 0判断未命中(依赖 ORM 行为) - 参数化查询是底线:永远别拼接 SQL 字符串,
"SELECT * FROM users WHERE username = '" + input + "'"是高危红线
实际部署时最容易被绕过的点,是没绑定 session/jwt 的上下文——比如 token 里只放了 user_id,却没绑 IP 和 User-Agent。一旦 token 泄露,攻击者能无缝接管。这事跟密码比对本身无关,但只要登录环节松一扣,前面所有 bcrypt 防护都白搭。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










