应直接使用 bcrypt.GenerateFromPassword 和 bcrypt.CompareHashAndPassword 处理密码,明文需转 []byte 且长度≤72字节,cost 推荐 bcrypt.DefaultCost(当前为12),哈希值存取须用 string() 转换并校验长度为60、前缀为 $2b$。
直接用
和
,别碰 AES、SHA256 或手写加盐逻辑。bcrypt 是密码哈希,不是加密——它不可逆,专为存密码设计;用错类型或参数,登录就静默失败。
明文密码传进去前必须转
且 ≤ 72 字节
Go 的
第一个参数强制要求
,传
直接编译报错。更隐蔽的问题是:UTF-8 编码后超 72 字节的部分会被静默丢弃,导致 "hello世界123..." 和它截断后的哈希值完全一样。
注册/登录入口必须校验:
中文、emoji、全角符号都算字节,不是字符数;前端限制可被绕过,服务端校验不可省
别依赖
判长度——它数的是 rune,不是字节
参数别硬写数字,优先用
截至 2026 年 4 月,
值为 12,对应单次哈希耗时约 150–250ms(主流云服务器),平衡了暴力破解成本与接口延迟。设成 10 太弱,GPU 几秒跑千万次;设成 14+ 可能单次超 1s,高并发下 goroutine 积压、HTTP 超时频发。
开发/测试环境用
加速?可以,但必须和生产配置隔离——否则压测看不出真实延迟
从配置读取
后,务必做范围校验:
迁移旧用户时,若 DB 里存的是
开头的哈希,需兼容处理,不能直接 panic
哈希值存数据库必须用
,字段至少
返回的是
,但内容是 ASCII 文本(如
),固定约 60 字节。直接存
到数据库,读出来大概率乱码或 decode 失败。
正确做法:
,再存进数据库
MySQL 字段类型用
,PostgreSQL 推荐
;
会截断,后续所有
都返回
从 DB 读出后,先
再传给比对函数——空格、BOM、换行都会让校验失败
调试时直接打长度:
,不是 60 就立刻查源头(入库逻辑 or ORM 自动 trim)
的调用顺序和前置检查不能漏
这个函数防时序攻击,但不帮你做任何业务逻辑判断。参数顺序反了、传空密码、哈希字段为空,它都可能返回
或错误,但行为不一致。
立即学习
“
go语言免费学习笔记(深入)
”;
参数顺序必须是:
,顺序反了会 panic
调用前显式判空:
DB 查询结果为
或
,应直接拒绝,别进比对流程
错误统一返回泛化提示(如 "invalid credentials"),日志中记录请求 ID + 哈希前缀即可,别打明文密码
真正容易被忽略的点是:哈希值在整条链路中是否被意外修改——从入库、JSON 序列化、ORM 映射、到读出比对,任何一个环节加空格、截断、base64 编码、或用了错误的字符集转换,都会让
静默失败。调试时第一反应不该是“算法写错了”,而是先确认
且开头是
。
bcrypt.GenerateFromPasswordbcrypt.CompareHashAndPassword[]bytebcrypt.GenerateFromPassword[]bytestringif len([]byte(pwd)) 72 { return errors.New("password length must be 8–72 bytes") }utf8.RuneCountInStringcostbcrypt.DefaultCostbcrypt.DefaultCostcost=4costif cost 31 { return errors.New("invalid bcrypt cost") }$2a$string(hashed)VARCHAR(255)bcrypt.GenerateFromPassword[]byte$2b$12$...[]bytehashedStr := string(hashed)VARCHAR(255)TEXTVARCHAR(50)CompareHashAndPasswordbcrypt.ErrMismatchstrings.TrimSpace()fmt.Println(len(hashStr))CompareHashAndPasswordnilbcrypt.CompareHashAndPassword([]byte(storedHash), []byte(inputPwd))if len(inputPwd) == 0 { return errors.New("empty password") }niluser.PasswordHash == ""CompareHashAndPasswordlen(hashStr) == 60$2b$