jwt签名密钥不能硬编码在代码里,因为会导致密钥随代码库版本控制、泄露后无法快速轮换、且反编译即可获取,使攻击者能伪造任意token;正确做法是通过环境变量注入,并启动时校验非空及长度(如hs256要求≥32字节)。

JWT签名密钥为什么不能硬编码在代码里
生产环境直接写死 jwt.SigningMethodHS256 配合一个字符串常量当密钥,等于把门锁换成贴纸。攻击者一旦拿到二进制或反编译代码,就能伪造任意 token。
正确做法是通过环境变量注入密钥,并在启动时校验非空和最小长度:
secret := os.Getenv("JWT_SECRET")
if secret == "" || len(secret)
- 密钥长度建议 ≥32 字节(HS256 对安全性有明确要求)
- 避免使用
os.Args或配置文件明文存储——它们同样可能被日志、备份或权限误配泄露 - Kubernetes 中用
Secret挂载;Docker Compose 用secrets字段,而非environment
ParseWithClaims 为什么总返回 “token is expired” 即使刚签发
常见原因是服务端时间与客户端不一致,或 token 的 exp 字段被设成秒级时间戳但解析时误用了毫秒时间戳。
Go 的 jwt.ParseWithClaims 默认使用 time.Now().Unix() 做校验基准,它返回的是秒级整数。如果你手动构造 exp 用了 time.Now().UnixMilli(),就会导致 token 立刻过期。
检查你的签发逻辑:
claims := jwt.MapClaims{
"uid": 123,
"exp": time.Now().Add(24 * time.Hour).Unix(), // ✅ 正确:秒级
// "exp": time.Now().Add(24 * time.Hour).UnixMilli(), // ❌ 错误:毫秒级,值大了1000倍
}
- 永远用
.Unix()设置exp/nbf/iat - 若需容忍轻微时间偏差,可传入自定义
jwt.ParserOption,比如jwt.WithValidTimeFunc(func(t time.Time) bool { return t.After(time.Now().Add(-5 * time.Second)) }) - 不要依赖客户端传来的
iat做业务判断——它可被任意篡改
如何安全地把用户 ID 从 token 里取出来又不 panic
直接类型断言 token.Claims.(jwt.MapClaims)["uid"] 在 token 无效、结构不符或字段缺失时会 panic,且无法区分是解析失败还是字段不存在。
必须分两步验证:先确认 token 有效且未过期,再安全取字段:
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(secret), nil
})
if err != nil || !token.Valid {
http.Error(w, "invalid token", http.StatusUnauthorized)
return
}
<p>// 此时 token.Claims 才可信
if claims, ok := token.Claims.(jwt.MapClaims); ok {
if uid, ok := claims["uid"].(float64); ok { // JSON number → float64
userID := int64(uid) // 注意:int64 而非 int,避免 32 位系统截断
// ✅ 安全使用 userID
}
}</p>
- 永远检查
token.Valid,不能只看err == nil - JSON 数字在 Go 的
MapClaims中是float64,强转前务必用类型断言判断 - 如果签发时存的是字符串 ID(如 UUID),取的时候也要按
string断言,别想当然转成数字
Refresh Token 机制里最容易漏掉的两个清理动作
仅生成新 access token 是不够的。无状态 JWT 本身不支持“主动注销”,但 refresh token 是有状态的,必须显式管理生命周期。
每次用 refresh token 换取新 access token 后,必须做两件事:
- 立即作废当前使用的 refresh token(例如从 Redis 删除
refresh:<hash></hash>,TTL 设为 0) - 给新签发的 refresh token 设置比 access token 更长的过期时间,但必须限制“最大续期次数”或“绝对过期时间”——否则用户登录一次就能永久有效
示例策略:refresh token 有效期 7 天,但每次刷新后重置其过期时间为“当前时间 + 7 天”,同时记录该 token 的首次签发时间,超过 30 天则拒绝刷新。
漏掉第一条,别人截获旧 refresh token 就能无限续期;漏掉第二条,长期有效的 refresh token 一旦泄露,危害远超单个 access token。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











