jose (JavaScript)JWT 库
错误信息
"exp" claim timestamp check failed
jose (JavaScript) — JWTExpired, code ERR_JWT_EXPIRED
含义
jose 的声明校验拒绝了这个令牌,因为按验证方的时钟,令牌的 exp 时间戳已经过去。签名检查此前已经通过——这纯粹是时效性检查失败。对于其他声明,jose 也使用同样的措辞模式:如果令牌在其 not-before 时间之前就被使用,会报告“"nbf" claim timestamp check failed”。
原因
令牌已超出其有效期
常见访问令牌本来就设计为短期有效。缓存令牌却不检查是否过期的客户端、在一条内含令牌的消息入队数小时后才处理它的队列消费者,或者带有硬编码令牌的测试夹具,都必然会遇到这个错误。
验证方的时钟快于颁发者的时钟
常见验证主机上几分钟的时钟漂移会缩短每个令牌的实际有效期,还可能导致刚签发的令牌被拒绝。容器沿用宿主机的时钟——一台时钟漂移的虚拟机会让其上的所有 Pod 同时出问题。
exp 中写入了错误的单位
偶见exp 必须是自纪元(epoch)以来的秒数(RFC 7519)。如果签发令牌的代码把除法写错了,或者传入了被序列化为毫秒的 Date 对象,生成的令牌要么一签发就已过期,要么有效期长达数千年。
修复方法
- 1.
添加有上限的时钟容差
jose 接受 clockTolerance 选项(秒数或人类可读的字符串)。把它设得小一些;它用来吸收时钟漂移,而不是用来延长有效期。
js import { jwtVerify } from "jose"; await jwtVerify(token, key, { algorithms: ["ES256"], clockTolerance: "30s", }); - 2.
改动任何设置之前,先打印时间线
比较 iat、exp 和验证方的当前时间。规律会告诉你原因:exp 已过去若干秒 → 刷新有问题;exp 早于 iat → 签发逻辑出错;exp ≈ 当前时间却仍失败 → 时钟偏差。
js import { decodeJwt } from "jose"; const { iat, exp } = decodeJwt(token); console.log({ iat: new Date(iat * 1000), exp: new Date(exp * 1000), now: new Date() });