github.com/go-jose/go-jose/v4(Square 版 go-jose 的延续)实现了 JWS、JWE、JWK 和 JWT,因此凡是 Go 服务之间交换加密或签名令牌的地方,都能见到它。4.1.6 版对 HS、RS、PS、ES 系列和 EdDSA 的签名与验证均成功;ES256K、Ed25519 和 Ed448 不是它能识别的算法名称。
第 4 版把算法列表改为必需:jwt.ParseSigned(token, nil) 会报错 no signature algorithms specified,因此只能按你选定的算法解析令牌。如果该列表中包含 none,未签名的令牌也能解析,但 Claims(key) 绝不会让它们通过验证——读取这类令牌的唯一途径是名称一目了然的 UnsafeClaimsWithoutVerification。
验证分成两步,陷阱就在这里。token.Claims(key, &claims) 检查签名并填充结构体——对于一小时前就已过期的令牌,它照样返回了声明,没有任何报错。过期时间、生效时间(not-before)、颁发者和受众只由带 jwt.Expected 值的 claims.Validate() 或 ValidateWithLeeway 检查,而不含 exp 的令牌连这一关也能通过,因此要像示例那样检查 Expiry 是否已设置。Validate 允许 jwt.DefaultLeeway,即一分钟的容差,检查也证实了这一点。
密钥是有类型的。以 PEM 文本为密钥的 HS256 令牌,用 *ecdsa.PublicKey 验证时报错 error in cryptographic primitive,但把同样的 PEM 字节作为密钥传入时却验证通过——应把密钥保存为解析后的 Go 密钥,而不是字节。ES256 会拒绝 P-384 密钥,报错 expected 256 bit key, got 384 bits instead。DER 编码的 ES256 签名与伪造的签名一样,报出同样笼统的 error in cryptographic primitive;JWT 调试器会显示签名长度。
对于加密令牌,JWE 查看器会显示头部和算法;对于密钥集,JWKS 检测会显示提供方发布的内容,JWK 转换器可以在 JWK 和 PEM 之间转换密钥。