jwt过期依赖exp字段的unix时间戳,服务器无法主动撤销已签发令牌,必须动态计算exp值并统一时区为utc,否则因时间差或硬编码导致频繁报“expired token”。

JWT签发时exp设成固定时间戳就出问题
本地测试看着正常,一上线就报「Expired token」,十有八九是 exp 字段写死了。比如写成 'exp' => 1760000000,这个时间戳对应的是 2025-10-08,服务器时间一过就失效。
实操建议:
-
exp必须用time() + 秒数动态计算,别硬编码 - PHP 时区必须统一设为 UTC:
date_default_timezone_set('UTC'),否则本地开发(如 Asia/Shanghai)和线上(常为 UTC)时间差 8 小时,exp校验直接失败 - 如果用
firebase/php-jwt,它内部会调用time()做比较,但前提是你的 PHP 运行环境时区已对齐
签名验证用 == 或 strcmp 就等于没校验
JWT 第三段 signature 是防篡改的核心,但很多人在校验时用 == 或 strcmp() 比较重算签名和原始签名,这会导致时序攻击漏洞——攻击者可逐字节爆破签名。
实操建议:
- 必须用
hash_equals($expected, $given),它是 PHP 内置的恒定时间比较函数 - 手写签名逻辑时,
hash_hmac('sha256', "$header.$payload", $secret, true)的第三个参数true不能漏,否则返回的是十六进制字符串,不是二进制签名 - 别信 header 里的
alg字段——攻击者可以改成none或HS256以外的算法,服务端必须白名单限定只接受HS256
Authorization Header 在 Apache 下拿不到
前端带了 Authorization: Bearer xxx,但 PHP 里 $_SERVER['HTTP_AUTHORIZATION'] 总是空。这不是 JWT 库的问题,是 Web 服务器拦截了该 Header。
实操建议:
- Apache 环境下,在
.htaccess或虚拟主机配置中加这段:
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
- 如果是 Nginx,需在 server 块里加:
proxy_set_header Authorization $http_authorization; - 验证是否生效:在 PHP 中打印
print_r($_SERVER),搜HTTP_AUTHORIZATION或AUTHORIZATION
token 过期后无法主动作废,只能等它自然失效
JWT 本质无状态,服务端不存 token 记录,所以用户改密码、登出、权限变更后,旧 token 仍能用到 exp 时间点。这不是 bug,是设计使然。
实操建议:
- 真要支持主动吊销,得引入外部状态存储——推荐 Redis,以
jti(JWT ID)为 key,设置和 token 相同的过期时间 - 签发时生成唯一
jti:'jti' => bin2hex(random_bytes(16)),并存入 Redis - 校验 JWT 前,先查 Redis:
if (redis->get("jti:{$jti}") === false) { return 'token revoked'; } - 注意:加黑名单会削弱 JWT 的无状态优势,只在必要场景(如金融、后台管理)启用
exp 换安全,就得配刷新机制;选长 exp 换体验,就得接受吊销延迟。没有银弹,只有权衡。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











