jsonwebtoken 提供三个调用来签名和验证 JSON Web Token:jwt.sign()、jwt.verify() 和 jwt.decode()。它通过 jws 和 jwa 包处理 JWS 层,用 node:crypto 完成密码学运算,因此密钥可以是 PEM 字符串、Buffer 或 KeyObject 实例。jwt.decode() 只解析令牌——它从不检查签名,从它读取声明的代码会信任发送方写入的任何内容。
第 9 版堵住了经典的算法混淆途径,检查在 9.0.3 上证实了这一点:不传 algorithms 选项时,允许的算法列表由密钥类型决定;以公钥文本为密钥的 HS256 令牌会被拒绝;只有当你列出 none 且完全不提供密钥时,alg: none 才会通过。即便如此,仍应传入 algorithms——它明确写出了约定,而且即使密钥类型将来发生变化也依然有效。
支持的算法止于 RFC 7518 定义的集合:没有 EdDSA、Ed25519 或 ES256K。颁发者用 Ed25519 签名的令牌——在 jose、Go 和 Rust 技术栈中很常见——在这里无法验证,应改用 jose。ES256 还要求 P-256 密钥;以这个算法名用 P-384 密钥签名会直接抛出异常,而不会生成一个会被其他库拒绝的令牌。
令牌中的 ECDSA 签名是 JWS 强制要求的 64 字节原始 r‖s 格式。如果令牌是用 openssl dgst、Java 的 SHA256withECDSA 或云 KMS 手动拼装的,其签名就是 DER 格式,验证会报错 "ES256" signatures must be "64" bytes——把令牌粘贴到 JWT 调试器中可以查看签名长度,再用 DER ⇄ 原始格式转换器修复。
有两个默认行为让很多团队感到意外,一个比预想的更严格,一个比预想的更宽松:clockTolerance 为零,因此服务器之间只要有几秒的时钟漂移,就会报出 jwt expired;exp 是可选的,因此签发时不带它的令牌永远不会过期。签名时应设置 expiresIn,验证时应设置 maxAge 或显式要求 exp;可以用 JWT 密钥检测工具检查 HMAC 密钥。