双因素认证(2FA)
在 Go 项目里实现 TOTP(基于时间的一次性密码,Time-based One-Time Password) 双因素认证(2FA),可以借助 Google Authenticator、Authy 等 App 扫描二维码生成 6 位动态验证码。
OTP 是 One-Time Password的简写,表示一次性密码。
HOTP 是HMAC-based One-Time Password的简写,表示基于HMAC算法加密的一次性密码。
是事件同步,通过某一特定的事件次序及相同的种子值作为输入,通过HASH算法运算出一致的密码。
TOTP 是Time-based One-Time Password的简写,表示基于时间戳算法的一次性密码。
是时间同步,基于客户端的动态口令和动态口令验证服务器的时间比对,一般每60秒产生一个新口令,要求客户端和服务器能够十分精确的保持正确的时钟,客户端和服务端基于时间计算的动态口令才能一致。
1. 基础原理
- TOTP 算法:基于 RFC 6238(在 HOTP 基础上加上时间因子)。
- 要素:
secret:为用户生成的密钥(通常 base32 编码存储在 DB)。timeStep:默认 30 秒。codeLength:默认 6 位数字。
- 用户使用 Google Authenticator 扫描二维码(包含账号和 secret),之后每 30 秒生成一个动态验证码。
2. 使用的库
推荐库:
github.com/pquerna/otp- 支持 TOTP/HOTP + 生成二维码(PNG)
安装:
go get github.com/pquerna/otp
go get github.com/pquerna/otp/totp3. 完整代码示例
生成用户绑定二维码
package main
import (
"fmt"
"log"
"os"
"github.com/pquerna/otp/totp"
"github.com/pquerna/otp"
)
func main() {
// 为用户生成一个 secret(保存在数据库)
key, err := totp.Generate(totp.GenerateOpts{
Issuer: "MyGoApp", // 应用名称(显示在 Google Authenticator 里)
AccountName: "[email protected]", // 用户账号
SecretSize: 32, // 密钥长度
Algorithm: otp.AlgorithmSHA1, // Google Auth 默认 SHA1
})
if err != nil {
log.Fatal(err)
}
// 打印 secret(存数据库)
fmt.Println("Secret:", key.Secret())
// 导出为 otpauth URL(生成二维码用)
fmt.Println("URL:", key.URL())
// 生成二维码 PNG 文件
img, err := key.Image(200, 200)
if err != nil {
log.Fatal(err)
}
file, _ := os.Create("qrcode.png")
defer file.Close()
err = png.Encode(file, img)
if err != nil {
log.Fatal(err)
}
fmt.Println("二维码已生成: qrcode.png (用 Google Authenticator 扫描)")
}验证用户输入的 TOTP
package main
import (
"fmt"
"time"
"github.com/pquerna/otp/totp"
)
func main() {
// 假设 secret 是数据库中存的用户专属密钥
secret := "JBSWY3DPEHPK3PXP" // 示例
// 用户输入的 6 位验证码(从前端传入)
userCode := "123456"
// 校验
valid := totp.Validate(userCode, secret)
if valid {
fmt.Println("✅ 验证成功,允许登录")
} else {
fmt.Println("❌ 验证失败")
}
// 也可以自定义时间容差(允许前后 1 个时间窗口)
valid = totp.ValidateCustom(userCode, secret, time.Now(), totp.ValidateOpts{
Period: 30,
Skew: 1, // 容忍用户时钟快/慢一个周期
Digits: 6,
Algorithm: totp.AlgorithmSHA1,
})
if valid {
fmt.Println("✅ 验证成功 (带时间容错)")
} else {
fmt.Println("❌ 验证失败 (带时间容错)")
}
}4. 项目中如何接入
- 用户绑定阶段:
- 后端为用户生成
secret和二维码。 - 用户扫码保存到 Google Authenticator。
- 后端存储用户 secret 到数据库(加密存储)。
- 后端为用户生成
- 登录阶段:
- 用户输入账号 + 密码 → 验证密码正确。
- 再输入 TOTP 验证码 → 调用
totp.Validate验证。 - 双因素认证通过 → 颁发 JWT / Session。
- 安全存储:
secret不要明文存储,建议用 AES 或 KMS 加密。- 如果用户需要重置 2FA,可以重新生成 secret。
最后更新于