必须将明文密码转为[]byte且长度≤72字节,哈希值需转string存入varchar(255)或text字段,比对前须校验空值、trim空白符及长度是否为60,cost参数生产环境推荐12或13并配置化管理。

直接用 bcrypt.GenerateFromPassword 和 bcrypt.CompareHashAndPassword 就行,但参数类型、存储方式、空值处理错一个,登录就静默失败。
明文密码必须转 []byte 且 ≤72 字节
Go 的 bcrypt 不接受 string,传进去会编译报错;更隐蔽的是:UTF-8 编码后超 72 字节的部分会被静默丢弃——“你好世界?123” 和它截断后的哈希完全一样。
- 注册/登录入口加校验:
if len([]byte(pwd)) 72,返回明确错误 - 别用
utf8.RuneCountInString(pwd)判长度,它数的是 rune,不是字节 - 中文、emoji、全角符号都按字节算,前端限制可被绕过,服务端校验不可省
哈希值存数据库前必须转 string(),字段至少 VARCHAR(255)
bcrypt.GenerateFromPassword 返回的是 []byte,内容是 ASCII 文本(如 b$...),固定约 60 字节。直接存二进制到数据库,读出来大概率乱码或 decode 失败。
- 正确写法:
hashedStr := string(hashed),再存入数据库 - MySQL 字段类型必须是
VARCHAR(255)或TEXT;VARCHAR(50)会截断,后续所有CompareHashAndPassword都返回bcrypt.ErrMismatch - PostgreSQL 推荐用
TEXT,避免长度限制引发的隐性截断
CompareHashAndPassword 前必须做三件事
这个函数只比对,不帮你做任何业务判断。传入非法哈希、空字符串、或未 trim 的值,它要么 panic,要么直接返回 hashedPassword is not the hash of the given password。
- 查用户后先判空:
if user.Password == "",直接拒绝,不进比对流程 - 从 DB 读出后立刻
strings.TrimSpace()—— 空格、BOM、换行都会导致校验失败 - 调用前检查长度:
if len(user.Password) != 60,说明入库或读取环节已损坏,立刻查源头
cost 参数别硬写数字,必须范围校验且区分环境
截至 2026 年 6 月,bcrypt.DefaultCost 是 12,对应单次哈希约 150–250ms(主流云服务器)。设成 10 太弱,GPU 几秒跑千万次;设成 14+ 可能单次超 1s,高并发下 goroutine 积压、HTTP 超时频发。
- 生产环境统一用
12或13,通过配置项注入,不要硬编码 - 配置读出后必须校验:
if cost bcrypt.MaxCost - 测试环境可用
4加速,但必须和生产配置隔离——否则压测看不出真实延迟
最常被忽略的是:哈希字符串本身是自包含结构(含版本、cost、salt、密文),不能只存后半段;也不能把明文当哈希传给 CompareHashAndPassword——调试时多打一行 fmt.Println(len(hash)),不是 60 就立刻停。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











