jwt-auth 插件已废弃,应改用 laravel/sanctum 或手写轻量 jwt;sanctum 开箱即用,适配 spa/移动端;手写推荐 firebase/php-jwt,注意签名、异常处理与无状态验证。

JWT-Auth 插件已废弃,别再装 tymon/jwt-auth
它从 Laravel 8+ 开始彻底不兼容,官方也明确归档。你现在执行 composer require tymon/jwt-auth,大概率会遇到依赖冲突或 Class 'Tymon\JWTAuth\Providers\LaravelServiceProvider' not found 错误——这不是你配置错了,是包本身已停更三年,PHP 8.1+ 和 Laravel 9/10 的容器绑定、异常处理机制全变了。
替代方案就一个:用官方维护的 laravel/sanctum(适合 SPA / 移动端 API),或手写轻量 JWT(仅需 20 行内核逻辑)。前者开箱即用,后者可控性强。别纠结“插件配置方法”,那套文档现在看等于教人修诺基亚按键机。
Laravel 9/10 用 Sanctum 实现无状态 JWT 风格认证
Sanctum 不生成传统 JWT,但行为一致:登录后返回 token,后续请求带 Authorization: Bearer {token},服务端验证签名和过期时间,且不依赖 session。它本质是“token + 签名验证 + 可选数据库校验”的组合,比 JWT 更安全(避免密钥泄露后全站失效)。
实操要点:
- 运行
composer require laravel/sanctum,然后执行php artisan sanctum:install(会建 migration 并发布配置) - 在
app/Models/User.php中混入HasApiTokenstrait - 登录接口里别调
Auth::attempt(),直接用$user->createToken('api')->plainTextToken返回 token 字符串 - 中间件用
auth:sanctum,不是auth:api—— 后者是旧 Passport 的遗留写法,会报TokenGuard missing authenticate method - 前端必须把 token 放进请求头:
headers: { 'Authorization': 'Bearer ' + token },漏掉Bearer前缀是高频 401 原因
自己手写 JWT(Laravel 10 + PHP 8.2,5 分钟可上线)
如果你需要真 JWT(比如要和外部系统互通、或必须用 HS256 签名),别碰第三方包。Laravel 自带 Illuminate\Support\Facades\Crypt 不适合签发,但 firebase/php-jwt 极轻量(仅 1 个类),且完全兼容现代 PHP。
步骤很直白:
- 装依赖:
composer require firebase/php-jwt - 生成 token 时用
JWT::encode($payload, config('app.key'), 'HS256'),注意config('app.key')是 32 字节字符串,别直接用字符串字面量 - 验证时捕获
DomainException、SignatureInvalidException、BeforeValidException这三类异常,它们对应密钥错、签名无效、token 没到生效时间 - payload 里别塞敏感字段(如密码哈希),
exp必须设(建议time() + 3600),否则验证永远失败 - 验证成功后,用
User::find($payload['sub'])查用户,别用Auth::loginUsingId()—— 它会写 session,破坏无状态前提
为什么 JWT 验证总返回 401?重点检查这三点
90% 的 401 不是逻辑错,是基础设施卡点:
- Apache 服务器默认过滤
Authorization头,需在.htaccess加RewriteCond %{HTTP:Authorization} ^(.*)和RewriteRule .* - [e=HTTP_AUTHORIZATION:%1] - Nginx 要显式透传:在
location块里加proxy_set_header Authorization $http_authorization; - Laravel 的
APP_URL配置必须带协议和端口(如https://api.example.com),否则 Sanctum 的stateful域名匹配失败,导致 cookie 不被发送
这些点藏在部署环节,开发时 localhost 下一切正常,一上预发就崩。查日志别只盯 storage/logs,先看 Nginx/Apache 的 access log 里有没有 Authorization 字段,没有就是头根本没进来。










