jwt.ParseWithClaims必须使用keyFunc而非直接传密钥,否则会跳过算法校验导致alg篡改攻击;必须显式检查token.Valid,且自定义Claims需嵌入jwt.RegisteredClaims并正确命名字段。

jwt.ParseWithClaims 必须配 keyFunc,不能只传密钥
直接传 []byte 给 jwt.ParseWithClaims 的第三个参数会跳过算法校验,导致 header 中的 alg 被篡改为 none 或不匹配类型时仍能解析成功。这不是“解析失败”,而是签名验证被绕过。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 始终用函数形式的
keyFunc,而非字面量密钥 -
keyFunc内必须做三件事:检查token.Method类型、拒绝非法算法、返回对应密钥 - 示例中若 token header 是
{"alg":"HS256"},但keyFunc返回了jwt.SigningMethodHS512对应的密钥,验签必然失败
token.Valid 为 false 时 err 可能仍是 nil
jwt.ParseWithClaims 成功只代表语法合法、算法可识别,不代表签名有效或未过期。常见错误是只判 err != nil 就放行,结果让过期、密钥错、alg 篡改的 token 进入业务逻辑。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 必须显式判断
if !token.Valid,不能依赖err -
token.Valid为 false 时,err可能是nil(比如过期),也可能非 nil(比如 signature invalid) - 标准字段如
ExpiresAt只有嵌入jwt.RegisteredClaims才会被自动校验;否则token.Valid永远为 true
自定义 Claims 必须嵌入 jwt.RegisteredClaims
如果结构体里只写 ExpiresAt int64,JWT 库不会把它当标准声明处理——过期时间不会触发自动校验,token.Valid() 也永远返回 true。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 正确写法:
type UserClaims struct { UserID string; jwt.RegisteredClaims } - 字段名必须严格为
ExpiresAt(不是exp或expire_at),且值要用jwt.NewNumericDate(time.Now().Add(...))包装 - 解析时传指针:
&UserClaims{},传值会导致字段赋值丢失 - 解析后取字段前,先做类型断言:
claims, ok := token.Claims.(*UserClaims),否则 panic
header.alg 篡改是真实攻击路径,不是理论风险
v4+ 版本默认禁用 none 算法,但攻击者仍可把 HS256 改成 HS512 或其他支持算法,再配合弱密钥爆破。如果你的 keyFunc 不校验 token.Method.Alg(),就等于开了个门。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 在
keyFunc开头加判断:if token.Method.Alg() != jwt.SigningMethodHS256.Alg() { return nil, errors.New("alg not allowed") } - 不要从 header 里读
kid去查密钥——这个字段本身未签名,必须等token.Valid == true后才可信 - 密钥必须 ≥32 字节(HS256 要求),硬编码或从环境变量读,别用短字符串


















