jwtauth::attempt() 返回 false 的根本原因是凭据未通过模型验证逻辑:密码未用 bcrypt 加密、字段名不匹配或模型未正确实现 jwtsubject 接口。

JWTAuth::attempt() 为什么总返回 false
根本原因通常是凭据没走对模型的验证逻辑:密码没加密、字段名不匹配、或模型没正确实现 JWTSubject 接口。
常见错误现象:JWTAuth::attempt(['email' => 'x', 'password' => 'y']) 返回 false,但 DB 查得到用户。
- 确认模型中
$table指向的表名和实际用户表一致(比如不是默认users而是test) - 密码必须用
bcrypt()加密存储;若用明文或其它哈希方式(如md5),attempt()会直接失败 -
getJWTIdentifier()返回值必须与数据库主键字段类型一致(如id是int,不能返回字符串"1") - 确保模型已实现
JWTSubject接口并正确重写两个方法 —— 缺少任一都会导致解析失败
JWTAuth::fromUser() 的使用限制
这个方法不查库,只依赖已有用户实例,但要求该实例必须“干净”:不能是 DB::table()->first() 这类原生查询结果,也不能是未加载关系或被修改过属性的模型。
典型场景:注册后立即签发 token,或后台管理员手动为某用户生成 token。
- 必须传入完整的 Eloquent 模型实例,例如
User::findOrFail(1)或auth()->user() - 若模型被
unset($user->password)或toArray()转成数组,fromUser()会抛出UnexpectedValueException - 注意时间戳字段(
created_at/updated_at)是否为Carbon实例;非对象类型可能触发序列化异常
token 刷新失败:refresh_ttl 和 ttl 的区别搞错了
refresh_ttl 不是“token 过期后还能续多久”,而是“从 token 签发起,最多允许刷新多少分钟”。它和 ttl 是独立计时的。
比如 ttl=60(1小时过期)、refresh_ttl=20160(2周),意味着:只要原始 token 是 2 周内签发的,哪怕已过期 59 分钟,仍可刷新;超过 2 周,哪怕刚过期 1 秒,也拒绝刷新。
- 刷新操作必须用
JWTAuth::parseToken()->refresh(),不能用attempt()重登 - 刷新前需确保当前 token 未被 blacklist(中间件
jwt.refresh默认启用 blacklist) - 如果
refresh_ttl设得太短(如 5 分钟),用户活跃时频繁刷新反而容易踩到窗口边界,建议设为ttl的 10–20 倍
中间件里 parseToken()->authenticate() 报 TokenExpiredException 却没进 exception handler
这是因为 JWTAuth::parseToken()->authenticate() 在 token 过期时直接 throw 异常,而 Laravel 默认的 Handler.php 只捕获 TokenInvalidException 和 JWTException,漏掉了子类。
真实调试路径:过期 token → TokenExpiredException → 未被捕获 → 500 错误而非 401。
- 在
app/Exceptions/Handler.php的render()方法里显式 catch\Tymon\JWTAuth\Exceptions\TokenExpiredException - 返回 JSON 响应时,务必调用
response()->json(..., 401),否则前端收不到标准错误码 - 别依赖
auth:api中间件自动处理——它只负责校验,不接管异常响应逻辑
ttl 或 secret 后,旧 token 立刻失效,这点很容易被忽略。











