用github.com/golang-jwt/jwt/v5验签需调用Parse并传入匹配算法的KeyFunc,显式检查token.Valid;生成时用jwt.RegisteredClaims设exp等标准字段,避免手写map或时间单位错误。

如何用 github.com/golang-jwt/jwt/v5 验签并解析 JWT
新版 jwt 包(v5)已从 github.com/dgrijalva/jwt-go 迁移,旧包有严重安全漏洞且不再维护。直接用 v5 是当前唯一稳妥选择。
验签和解析必须同步完成:仅调用 Parse 不会验证签名,必须传入 KeyFunc 并确保返回的 key 与算法匹配,否则 Valid 字段为 false。
-
Parse返回的*Token需显式检查token.Valid,不能只看是否 panic 或 error 为 nil - 若使用 HS256,
KeyFunc应返回[]byte;若用 RS256,则返回*rsa.PublicKey - 常见错误是把密钥硬编码成字符串后忘了
[]byte("mykey"),导致签名始终不匹配
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("your-secret-key"), nil
})
if err == nil && token.Valid {
claims := token.Claims.(jwt.MapClaims)
userID := claims["user_id"].(float64) // 注意:数值型 claim 默认是 float64
}如何安全生成 HS256 JWT 并设置标准字段
生成 JWT 时,别手写 map[string]interface{} 填 claims——容易漏掉 exp、拼错 iat,或误用时间戳单位。
- 用
jwt.RegisteredClaims构建标准头,它自动处理exp、iat、nbf的 Unix 时间秒级整数格式 -
exp必须是time.Time,不是秒数;设为time.Now().Add(24 * time.Hour),不是time.Now().Unix() + 86400 - 自定义字段建议放在
map[string]interface{}外层结构体中,避免和标准字段命名冲突(如不要叫iss或sub)
claims := jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Subject: "user:123",
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString([]byte("your-secret-key"))
为什么 Parse 成功但 token.Valid == false
这是最常被忽略的“假成功”场景:函数没 panic、error 为 nil,但 token 实际未通过校验。
立即学习“go语言免费学习笔记(深入)”;
- 典型原因:key 返回了空值、类型不匹配(比如 RS256 却返回了 []byte)、或
exp/nbf时间已过期/未生效 -
token.Claims在Valid == false时仍可强制类型断言,但数据不可信——尤其exp过期后,MapClaims里的exp值仍是原始数字,不代表当前有效 - 调试时可打印
token.UnverifiedClaims()(v5.1+)看原始 payload,再比对token.Claims是否被篡改或解析异常
RS256 签名下如何加载 PEM 公钥文件
用 RSA 验签时,公钥必须从 PEM 块中正确解码,不能直接读文件内容后强转 *rsa.PublicKey。
- 先用
io.ReadFile读取公钥文件(如public.pem),再用pem.Decode提取 block - 用
x509.ParsePKIXPublicKey解析,而非ParsePKCS1PublicKey—— 后者只支持 PKCS#1 格式,而大多数 JWT 公钥是 PKIX/PKCS#8 - 若遇到
crypto/rsa: verification error,大概率是公钥格式或解析方式不对,不是私钥签名问题
pubKeyData, _ := os.ReadFile("public.pem")
block, _ := pem.Decode(pubKeyData)
pubKeyInterface, _ := x509.ParsePKIXPublicKey(block.Bytes)
return pubKeyInterface.(*rsa.PublicKey), nilJWT 的核心陷阱不在语法,而在时间语义、密钥类型匹配和验证路径的完整性。哪怕只漏掉一次 token.Valid 检查,或把 exp 设成毫秒时间戳,都会让整个鉴权逻辑形同虚设。










