JWT::decode() 已自动校验 exp、nbf、iat,无需手动比对;exp 必须为 int 型 Unix 时间戳,缺失时默认跳过校验致永不过期,需用 isset() 和 is_int() 双重确认。

JWT 的过期验证不是“自己比对 exp 字段”就完事的——firebase/php-jwt 库在 decode() 内部已自动校验 exp、nbf、iat,手动再比一次不仅多余,还可能因时区、类型或逻辑顺序出错。
JWT::decode() 已内置过期检查,别重复写 $decoded->exp
很多人看到文档示例里有手动判断 exp,就照搬进生产代码,结果反而绕过库的异常机制,让本该抛 ExpiredException 的情况静默失败。真实行为是:
-
JWT::decode()在解析成功前,会严格按 RFC 7519 校验时间字段:若exp存在且 ≤time(),直接抛ExpiredException - 若
nbf存在且 >time(),抛BeforeValidException - 若
iat存在但 >time()(即签发时间在未来),也会触发校验失败 - 手动再写
if ($decoded->exp 属于重复劳动,且容易忽略 <code>isset($decoded->exp)判空,导致 Notice 错误
exp 必须是 int 型 Unix 时间戳,不能是字符串或 float
常见错误是把 date('Y-m-d H:i:s') 或 strtotime('+1 hour') 直接塞进 payload——这些返回值类型不稳定,firebase/php-jwt 的校验逻辑只认整数型时间戳:
-
exp字段必须用time() + 3600这类表达式,确保结果为int -
var_dump(gettype($payload['exp']))应输出integer,不是string或double - 如果从数据库或缓存读取
exp,务必用(int)$stored_exp强转,避免字符串比较(如"1747949040" 在 PHP 中可能为 true) - Docker 环境下要确认容器时钟与宿主机同步,
ntpq -p或timedatectl status可查
分布式部署时,exp 偏差比你想象中更敏感
单机开发时 time() + 3600 看似稳妥,但在多台服务器间时钟不同步(哪怕只有 200ms 差异),就可能导致 token 在 A 机生成后立刻被 B 机判为过期:
立即学习“PHP免费学习笔记(深入)”;
- 不要依赖各节点本地
time()计算exp;若需强一致性,改用 NTP 同步所有节点,或引入中心化时间服务(如 Redis 的TIME命令) -
exp值建议预留缓冲,比如设为time() + 3540(而非刚好 3600),给时钟漂移留余量 - 禁用
none算法,防止攻击者篡改 payload 后伪造exp;decode()第三个参数必须显式传['HS256'],不能留空 - 高并发场景下,
time()调用本身极轻量,但反复调用仍建议缓存一次:$now = time(); $payload = ['exp' => $now + 3600, 'iat' => $now];
真正容易被忽略的,是 exp 字段缺失时的默认行为——decode() 不会报错,而是跳过校验,token 永不过期。上线前务必用 isset($payload['exp']) 和 is_int($payload['exp']) 双重确认。



















