必须将 config/auth.php 中 api guard 的 driver 改为 jwt,否则 auth:api 中间件仍使用 token 驱动查 personal_access_tokens 表,无法识别 jwt;同时需确保服务提供者已注册,并正确使用 jwt.auth 中间件而非 auth:api。

JWT 在 Laravel 里不是“装完包就能用”,而是必须显式替换 auth:api guard 的底层驱动,否则 Auth::user() 永远为 null,哪怕请求头带了 Authorization: Bearer xxx。
为什么 auth:api 中间件不认你的 JWT Token
默认的 api guard 使用的是 Laravel 自带的 token 驱动,它查数据库里的 personal_access_tokens 表,和 JWT 完全无关。你装了 tymon/jwt-auth 或 php-open-source-saver/jwt-auth,它不会自动接管 auth:api——必须手动改配置。
- 打开
config/auth.php,找到'guards' => ['api' => [...]],把'driver' => 'token'改成'driver' => 'jwt' - 确保该 driver 已注册:Laravel 9+ 的
php-open-source-saver/jwt-auth不再自动注册服务提供者,得在config/app.php的providers数组里手动加一行Tymon\JWTAuth\Providers\LaravelServiceProvider::class - 如果仍返回
401或Auth::user()为空,检查是否误用了middleware('auth:api')—— 正确写法是middleware('jwt.auth')(对应\Tymon\JWTAuth\Middleware\GetUserFromToken::class)
登录接口返回空 token 或 TokenCouldNotBeCreatedException
这个错误和密码错没关系,只说明 JWTAuth::attempt() 拿不到合法用户实例。根本原因通常是 User 模型没正确实现 JWTSubject 接口,或返回了非法标识。
-
User类必须use Tymon\JWTAuth\Contracts\JWTSubject并implements JWTSubject -
getJWTIdentifier()必须返回标量值(如$this->getKey()),不能是$this->id(Eloquent 属性访问可能触发延迟加载或返回 null) -
getJWTCustomClaims()可以返回空数组,但方法体不能缺失 - 若登录字段不是
email/password(比如用username),JWTAuth::attempt($request->only('username', 'password'))会失败,因为默认只校验email字段;需重写模型的findForPassport()或在登录逻辑里手动查用户再调用JWTAuth::fromUser($user)
Token 刷新失败或被拒绝:refresh_ttl 和黑名单陷阱
JWTAuth::refresh() 不是从请求头自动读 Token 的,它需要当前有效的 token 作为输入;而且刷新成功后,旧 token 默认进入黑名单——但前提是黑名单驱动已启用且配置正确。
- 调用
refresh()前,必须先从请求中提取原始 token(如$token = request()->bearerToken()),再传入:JWTAuth::refresh($token) - 检查
config/jwt.php中'blacklist_enabled' => true,且'storage' => 'cache'(推荐)或'database';若用cache驱动但缓存驱动是array(开发环境默认),黑名单实际不生效 -
refresh_ttl是刷新窗口期(默认 14 天),不是新 token 的有效期;新 token 的有效期由ttl控制;若用户在ttl过期后、refresh_ttl内发起刷新,会失败并报TokenExpiredException - Apache 用户注意:若请求头
Authorization: Bearer xxx无法被 PHP 读取,可能是服务器重写规则丢掉了 header,需在.htaccess加RewriteCond %{HTTP:Authorization} ^(.*)$和RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
第三方 JWT(如 Auth0、AWS Cognito)验证失败
验证外部签发的 JWT 时,tymon/jwt-auth 默认用 HS256 + JWT_SECRET 验签,但第三方普遍用 RS256 + 公钥,直接复用原配置必然失败。
- 修改
config/jwt.php:'algo' => 'RS256',并设置'keys' => ['public' => 'file://'.storage_path('jwt/public.pem')],其中public.pem是从 JWKS URL 下载并转换后的公钥文件 - 删掉
'private'和'passphrase'配置项(验证不需要私钥) - 标准声明(
iss,aud,exp)必须匹配:在config/jwt.php的'required_claims'数组里补全,例如['iss', 'aud', 'exp'];同时在'allowed_providers'或自定义中间件里硬编码比对iss值 - 别依赖
auth:api中间件做这事——它压根不支持自定义验签逻辑;应新建一个中间件,用JWTAuth::setToken($token)->parseToken()->validate()手动控制全流程
最常被忽略的一点:JWT 认证异常(比如签名无效、过期、黑名单命中)会中断中间件链,导致 CORS 头丢失,前端看到的是 “No 'Access-Control-Allow-Origin' header” 而不是真实的错误信息。这个问题不在 JWT 包里,而在异常响应的兜底处理上。











