结论:用 firebase/php-jwt 是当前 PHP 项目中最稳妥、文档最清晰、错误提示最友好的 JWT 实现,别自己手撸 base64UrlEncode + HMAC;JWT::encode() 第三个参数算法名(如'HS256')必填且须显式指定、大小写敏感、与密钥类型严格匹配,否则抛 InvalidArgumentException 或 DomainException。

直接说结论:用 firebase/php-jwt 是当前 PHP 项目中最稳妥、文档最清晰、错误提示最友好的 JWT 实现,别自己手撸 base64UrlEncode + HMAC。
为什么不能直接用 JWT::encode() 传数组而不指定算法?
会抛出 InvalidArgumentException —— 因为 JWT::encode() 的第三个参数(算法名)是必填的,不是可选默认值。PHP-JWT 不会自动 fallback 到 HS256,它要求你显式声明。
- 常见错误写法:
JWT::encode($payload, $key)→ 报错 - 正确写法:
JWT::encode($payload, $key, 'HS256') - 如果传了不支持的算法名(比如
'HS256X'),会触发DomainException - 注意:算法名必须全大写、带引号,且与密钥类型匹配;用 RS256 就不能传对称密钥字符串,得传
new Key($pem, 'RS256')
JWT::decode() 报 SignatureInvalidException 的真实原因
这错误看起来像“签名错了”,但实际 70% 是因为密钥没对上,或用了错误的密钥类型。
- 用 HS256 签发的 token,却用
new Key($pem_content, 'RS256')去验 → 必报此错 - 密钥字符串前后有空格或换行(尤其从环境变量读取时)→ 解码失败
- token 被前端意外截断(比如 URL 中未编码的
+变成空格)→ 签名校验自然失败 - 推荐做法:先用
JWT::getPayload($jwt)(不验签)看 header 和 payload 是否可解析,排除格式问题
如何安全地从 Authorization 头提取 token?
别直接用 $_SERVER['HTTP_AUTHORIZATION'],Apache 下这个变量经常为空。
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
立即学习“PHP免费学习笔记(深入)”;
- 更健壮的写法:
$auth = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? ''; - 然后正则匹配:
if (preg_match('/^Bearer\s+(?<token>[^\s]+)$/i', $auth, $matches)) { $jwt = $matches['token']; }</token> - 注意:
$jwt必须是完整三段式字符串(含两个点),少一段或多了空格都会让JWT::decode()抛UnexpectedValueException - 不要在验证前做 trim() 或 urldecode() —— JWT 的 base64url 编码本身已处理特殊字符,手动解码反而破坏格式
过期时间 exp 校验为什么有时“明明没过期却报错”?
根本原因是服务器时间不准,或客户端/服务端时区不一致导致的 time() 偏差。
-
JWT::decode()内部调用的是time(),不是$_SERVER['REQUEST_TIME_FLOAT'] - 如果服务器时间比标准时间慢 2 分钟,而 token 的
exp是 1 小时后,那实际只剩 58 分钟时就被判为“已过期” - 解决办法:部署 NTP 服务同步时间;或临时放宽校验(不推荐):
JWT::setLeeway(60)允许最多 60 秒误差 - 注意:
setLeeway()是静态方法,需在 decode 前调用,且只影响exp和nbf校验
真正容易被忽略的是密钥管理——$secretKey 绝不能硬编码在代码里,也不能通过 getenv() 直接暴露给所有请求上下文;轮换密钥时,旧 token 怎么平滑过渡,才是线上系统最常翻车的地方。


















