必须用github.com/pquerna/otp,因其严格遵循RFC 6238、兼容所有主流Authenticator、无隐藏依赖;误选boombuler/otp(仅HOTP)或手搓实现会因时间窗口、编码错误导致验证失败。

直接用 github.com/pquerna/otp,别自己手搓或误选 boombuler/otp —— 后者只支持 HOTP,而 Google Authenticator 默认走 TOTP,扫码后验证必失败。
为什么必须用 pquerna/otp 而不是其他库
Go 生态里真正兼容 RFC 6238、能和 Google/Microsoft/Authy 等所有主流 Authenticator App 互通的库极少。pquerna/otp 虽已归档,但代码稳定、无运行时反射、无隐藏依赖,Gin/Echo 社区大量 2FA 中间件都基于它。误选 boombuler/otp 会卡在“扫码成功但输啥都不对”,因为它压根不处理时间戳,只认计数器;自己用 hmac + base32 手搓则极易在填充规则、时间窗口偏移、秒级/毫秒级时间戳上出错。
otp.GenerateTOTP 生成密钥时的三个硬性要求
密钥不能是随机字符串,也不能用 hex 编码展示给用户:
- 必须调用
otp.GenerateTOTP生成原始字节密钥,而不是rand.Read或uuid.NewString - 存储和展示前,必须用
base32.StdEncoding.EncodeToString编码 —— Google Authenticator 只认 Base32,不是 Base64 也不是 raw bytes - 生成二维码时,URI 格式必须为
otpauth://totp/YourApp:username?secret=XXX&issuer=YourApp&algorithm=SHA1&digits=6&period=30,缺任意参数(尤其是issuer)可能导致某些 App 解析失败
otp.ValidateTime 验证失败的三大落地原因
用户扫完码输 6 位数却一直提示“无效”,90% 是以下三者之一:
Google Calendar API 快速入门:创建项目、启用 API、设置 OAuth 2.0 凭据。本指南将引导您完成下载凭据文件并将其用于访问 API 的全过程。
立即学习“go语言免费学习笔记(深入)”;
- 服务器时间没同步:偏差超过默认 30 秒窗口 → 运行
sudo ntpdate -s time.nist.gov或启用systemd-timesyncd - 传错时间戳:
otp.ValidateTime第二个参数必须是time.Now().Unix()(秒),不是.UnixMilli()或.UnixNano() - 密钥没解码就直接传:前端 POST 上来的
secret是 Base32 字符串,验证前必须先base32.StdEncoding.DecodeString成[]byte,否则 HMAC 计算完全错位
密钥存储和传输的不可妥协点
2FA 密钥比密码更敏感——它一旦泄露,攻击者无需密码就能通过验证。所以:
- 绝不能明文存数据库;至少 AES 加密(密钥由 KMS 或环境变量管理)
- 前端传输必须走 HTTPS,且禁止在 URL、日志、前端 JS 变量中暴露原始密钥字符串
- 用户首次绑定后,服务端应立即丢弃未加密的临时密钥副本,只保留 Base32 编码后的密文和加密后的原始字节
最常被跳过的细节是时间戳单位和密钥解码顺序——这两个点错一个,验证就永远返回 false,而且不会报错,只会静默失败。

















