直接安装 firebase/php-jwt:执行 composer require firebase/php-jwt,再在控制器中 var_dump(class_exists('firebase\\jwt\\jwt')) 输出 true 即确认可用;密钥须从配置读取,payload 必含 iat/exp(int 类型时间戳),encode 第三参数算法名不可省,中间件需设 jwt::$leeway = 5 并正确截取 bearer 前缀。

直接用 firebase/php-jwt,别碰 tymon/jwt-auth 或手写签名——ThinkPHP6 没原生 JWT 支持,第三方库里只有 firebase/php-jwt 被长期验证、RFC 合规、无历史安全漏洞。
怎么装 firebase/php-jwt 并确认可用
执行 composer require firebase/php-jwt 后,必须验证类是否能加载。在任意控制器里加一行:var_dump(class_exists('Firebase\JWT\JWT'));,输出 true 才算真正就位。常见失败原因是 autoloader 没生效(比如 vendor 目录权限异常或 composer install 被中断),此时 require vendor/autoload.php 也救不了——得重装。
- 不要在
config/app.php或common.php里提前use FirebaseJWTJWT,TP6 自动加载机制不保证全局命名空间可用 - 密钥必须从配置读取,例如
config('jwt.secret'),而不是写死在new Key('xxx', 'HS256')里 - 如果项目用了多应用(如
api/和admin/),config/jwt.php要放在config/根目录,不能只放某应用下
JWT::encode() 必须带的 payload 字段和参数陷阱
iat 和 exp 是硬性要求,缺一就会让 JWT::decode() 抛 DomainException;exp 值必须是 int 类型时间戳,传字符串(如 '1715074000')会静默失败,token 看似生成成功,但 decode 时直接崩。
-
JWT::encode($payload, $key, 'HS256')第三个参数不能省,必须显式传算法名字符串 - payload 中避免塞
password、phone、id_card这类字段——JWT 是 base64url 编码,不是加密,前端可直接解出明文 - 密钥建议用
bin2hex(random_bytes(32))生成,至少 32 字节;md5('dd')这种弱密钥在生产环境等于裸奔
中间件里 JWT::decode() 报错类型与对应处理
90% 的鉴权中断不是逻辑错,而是没分清异常类型就统一 catch Exception。不同报错意味着完全不同的修复方向:
-
UnexpectedValueException: Signature verification failed→ 密钥不一致:检查config/jwt.php的secret是否和 encode 时用的完全相同(注意前后空格、换行、是否被 config 缓存覆盖) -
DomainException: Invalid token format→ Authorization 头格式不对:必须是Bearer xxx,且要先用str_starts_with($auth, 'Bearer ')判断,再trim(substr($auth, 7))截掉前缀,否则整个字符串传给 decode 就挂 -
ExpiredException→ 不只是过期:服务器时间不准也会触发;务必设FirebaseJWTJWT::$leeway = 5;容忍 5 秒偏差,且过期后应查 Redis 黑名单,而非直接拒掉 - 静默失败(
$request->getAttribute('uid')为空)→ 中间件漏了return $next($request);,ThinkPHP 中间件必须显式返回响应或继续流程
如何让 Auth 权限判断兼容 JWT 和 Session
别动 thinkAuth 类本身,它被 RBAC 逻辑强依赖。正确做法是新建 appcommonserviceJwtAuth.php,实现和 thinkAuth 同签名的 check($rule, $uid = null) 方法,在里面调用原 thinkAuth::check($rule, $uid ?? $this->getUidFromToken())。
- 用户 ID 必须从 token payload 提取(如
$decoded->data->uid),不能从 session 或 cookie 读 - 全局中间件中解析成功后,必须调用
$request->withAttribute('uid', $uid)写入,后续所有控制器、服务、模型都通过$request->getAttribute('uid')获取 - 模型事件、命令行任务、定时任务里禁止调用任何 JWT 解析逻辑——HTTP 请求头不存在,
$_SERVER['HTTP_AUTHORIZATION']是 null,JWT::decode()必崩
最易被忽略的是 leeway 设置和 Bearer 前缀截取——这两个点不出问题时一切正常,一出就是线上静默 401,连日志都不报具体原因。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











