跳到主要内容
ecdsa.com
Go

golang-jwt/jwt

golang-jwt/jwt,dgrijalva/jwt-go 的社区维护后继者,也是 Go 中使用最广的 JWT 包:验证按密钥类型进行,none 只能通过显式的哨兵值启用,过期时间除非你要求,否则是可选的。
许可证
MIT
检查的版本
5.3.1 ·
源代码
github.com/golang-jwt/jwt
文档
pkg.go.dev/github.com/golang-jwt/jwt/v5

兼容性概览

JOSE 与 JWT

JWS 算法
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

github.com/golang-jwt/jwt/v5 使用标准库中的密钥解析、校验和签名 JWT:HMAC 用 []byte,以及 *rsa.PublicKey、*ecdsa.PublicKey 和 ed25519.PublicKey。5.3.1 版对 HS、RS、PS、ES 系列和 EdDSA 的签名与验证均成功;ES256K 以及完全指定的 Ed25519 和 Ed448 算法名称都没有注册对应的签名方法。

验证的关键在于传给 jwt.Parse 的 keyfunc。每种签名方法都会检查收到的密钥的 Go 类型,因此当 keyfunc 返回 *ecdsa.PublicKey 时,RS256 令牌报错 RSA verify expects *rsa.PublicKey,以 PEM 文本为密钥的 HS256 令牌报错 HMAC verify expects []byte。如果 keyfunc 对每个令牌都返回原始字节,这层防护就不复存在——在检查中,把 PEM 文件加载为 []byte 的 keyfunc 让那个伪造的 HS256 令牌验证通过了。应像示例那样,用 jwt.WithValidMethods 传入确切的算法,并让 keyfunc 返回有类型的密钥。

只有当 keyfunc 返回哨兵值 jwt.UnsafeAllowNoneSignatureType 时,alg: none 才能验证通过;返回任何真实密钥都会得到 'none' signature type is not allowed。ES256 在签名时拒绝 P-384 密钥,只给出简单的 key is invalid。ES256 签名是 JWS 要求的 64 字节原始 r‖s;DER 签名会报错 crypto/ecdsa: verification error,可以借助 JWT 调试器和 DER ⇄ 原始格式转换器排查。

v5 默认开启声明校验,但 exp 是可选的——不含它的令牌永远有效——而且没有容差。jwt.WithExpirationRequired()、jwt.WithIssuer、jwt.WithAudience 和 jwt.WithLeeway 可以补上这些缺口;错误可以用 errors.Is(err, jwt.ErrTokenExpired) 匹配,而不必比较字符串。

要验证身份提供商签发的令牌,可将它与 JWKS 客户端搭配使用,并用 JWKS 检测查看提供商发布的内容;需要处理 JWE 或 JWK 时,可以改用 go-jose。

事实及其依据的检查

以下内容均非抄自文档。检查脚本 content/libraries/verify/golang-jwt/run.sh 于2026年10月20日在容器镜像 golang:1.27.3-trixie 中运行了 golang-jwt/jwt 5.3.1;本页内容必须与它的输出一致,每条事实都列出了对应的输出行。

  • JWS 算法

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

    256/384/512 位的 HS、RS、PS 和 ES 以及 EdDSA 的签名与验证均成功;ES256K、Ed25519 和 Ed448 没有注册的签名方法。

    检查记录
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, EdDSA
    evidence jws-algorithms: ES256K: no signing method registered
    evidence jws-algorithms: Ed25519: no signing method registered
    evidence jws-algorithms: Ed448: no signing method registered
  • ES256 签名编码

    raw

    ES256 签名是 64 字节的原始 r‖s。

    检查记录
    fact jws-ecdsa-encoding = raw
  • JWS 中的 DER 签名

    rejected

    DER 编码的 ES256 签名会报错 token signature is invalid: crypto/ecdsa: verification error。

    检查记录
    fact jws-der-signature = rejected
    evidence jws-der-signature: token signature is invalid: crypto/ecdsa: verification error
  • 算法允许列表

    from-key

    不使用 WithValidMethods 时,由密钥的 Go 类型决定:用返回 EC 密钥的 keyfunc 验证 RS256 令牌,报错 key is of invalid type: RSA verify expects *rsa.PublicKey。

    检查记录
    fact jwt-algorithm-allowlist = from-key
    evidence jwt-algorithm-allowlist: RS256 token, keyfunc returning the EC public key, no WithValidMethods: token signature is invalid: key is of invalid type: RSA verify expects *rsa.PublicKey
  • 未签名的令牌(alg none)

    opt-in

    除非 keyfunc 返回 jwt.UnsafeAllowNoneSignatureType,否则未签名的令牌会报错 'none' signature type is not allowed。

    检查记录
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: with the EC key: token signature is invalid: token is unverifiable: 'none' signature type is not allowed
    evidence jwt-alg-none: keyfunc returning jwt.UnsafeAllowNoneSignatureType: <nil>
  • 把公钥用作 HMAC 密钥

    rejected

    以 PEM 文本为密钥的 HS256 令牌,用返回 *ecdsa.PublicKey 的 keyfunc 验证失败(HMAC verify expects []byte),但当 keyfunc 返回 PEM 字节时验证通过。

    检查记录
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: keyfunc returning *ecdsa.PublicKey: token signature is invalid: key is of invalid type: HMAC verify expects []byte
    evidence jwt-hmac-public-key: keyfunc returning the PEM bytes (a keyfunc that loads keys as bytes): <nil>
  • 按 alg 检查曲线

    enforced

    用 P-384 密钥签名 ES256 时报错 key is invalid。

    检查记录
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: key is invalid
  • 默认时钟偏差

    0s

    默认没有容差:三秒前过期的令牌被拒绝。

    检查记录
    fact jwt-clock-skew = 0s
  • 是否要求过期时间声明

    no

    不含 exp 的令牌被视为有效;使用 WithExpirationRequired() 后,它会报错 token is missing required claim: exp claim is required。

    检查记录
    fact jwt-exp-required = no
    evidence jwt-exp-required: with WithExpirationRequired: token has invalid claims: token is missing required claim: exp claim is required

用法

用 ES256 对注册声明签名,再用有类型的 keyfunc 解析回来:锁定签名方法,并强制执行颁发者、过期时间和容差规则。
package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"fmt"
	"time"

	"github.com/golang-jwt/jwt/v5"
)

func main() {
	key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	claims := jwt.RegisteredClaims{Subject: "user-42", Issuer: "https://auth.example.com", ExpiresAt: jwt.NewNumericDate(time.Now().Add(10 * time.Minute))}
	signed, _ := jwt.NewWithClaims(jwt.SigningMethodES256, claims).SignedString(key)

	// The keyfunc returns a typed key; the options pin the method and the claims.
	token, err := jwt.ParseWithClaims(signed, &jwt.RegisteredClaims{},
		func(*jwt.Token) (any, error) { return &key.PublicKey, nil },
		jwt.WithValidMethods([]string{"ES256"}),
		jwt.WithIssuer("https://auth.example.com"),
		jwt.WithExpirationRequired(),
		jwt.WithLeeway(30*time.Second),
	)
	fmt.Println("valid:", err == nil && token.Valid, "sub:", token.Claims.(*jwt.RegisteredClaims).Subject)
}

代码片段测试工具会在容器镜像 golang:1.27.3-trixie 中原样运行每个示例,只要输出不匹配,运行就会失败。

命令
GOFLAGS=-mod=mod go run .
输出必须匹配(正则表达式)
valid: true sub: user-42

库