PyJWT (Python)JWT 库
错误信息
Signature has expired
PyJWT (Python) — jwt.exceptions.ExpiredSignatureError
含义
令牌的 exp 声明早于验证方的当前时间,因此 PyJWT 在签名检查(从概念上说)通过之后拒绝了它。这条消息的措辞有些误导——签名本身不会过期,是令牌的有效期已经结束。
原因
旧令牌在有效期结束后仍被使用
常见长时间运行的工作进程中缓存的凭证、存储在客户端并在 TTL 过后被重放的令牌,或者集成测试使用的、几天前生成的夹具令牌。这种拒绝是正确的行为。
验证方主机的时钟漂移
常见时钟走快的验证方会缩短每个令牌的实际有效期。嫌疑最大的通常是没有做时间同步的容器和 CI 运行器;即使只有 60~120 秒的漂移,也会让短期令牌间歇性地验证失败。
颁发者写错了 exp
偶见exp 必须是自纪元(epoch)以来的整数秒数。传入未正确序列化的 datetime 对象,或者传入毫秒数,会生成一签发就已过期的令牌——此后每次验证都会立即失败,这一点可以把它与普通的过期区分开。
用不带时区的 datetime 生成 exp
偶见如果颁发者用 datetime.now()(不带时区的本地时间)而不是 datetime.now(timezone.utc) 来生成 exp,每个令牌的有效期都会偏移服务器的 UTC 时差。根据颁发者位于 UTC 的哪一侧,令牌要么提前几小时过期,要么悄无声息地超出预定的 TTL 仍然有效。PyJWT 能正确编码带时区的 datetime——不带时区的才是陷阱。
修复方法
- 1.
用容差吸收少量时钟偏差
leeway 参数(单位为秒)作用于 exp 和 nbf 检查。最多设为几十秒;更大的值会悄悄延长令牌的有效期。
python import jwt payload = jwt.decode( token, key, algorithms=["ES256"], leeway=30, # seconds of tolerated clock skew ) - 2.
查看实际的时间戳
不经验证读取声明,把 exp 与主机时钟比较——差距的大小可以告诉你这是正常过期、时钟偏差,还是签发逻辑的错误。
python import jwt, time claims = jwt.decode(token, options={"verify_signature": False}) print("exp:", claims["exp"], "now:", int(time.time()), "delta:", int(time.time()) - claims["exp"], "s")