jwt认证在laravel中需手动配置guard、provider及Token签发/解析逻辑,非开箱即用;auth::user()在api路由中为NULL主因是guard未正确切换至jwt或sanctum,且api中间件组误含startsession;token须存客户端并以authorization: bearer xxx格式传递,过期需结合refresh机制与黑名单校验。

JWT认证在Laravel里不是开箱即用的
Laravel自带的 Auth::attempt() 和 Session 认证不生成 JWT,直接装个包就以为能用 Auth::user() 拿到登录用户?大概率 401 或 null。JWT 是无状态的,得自己接管 token 的签发、解析、刷新逻辑,Laravel 的 guard 和 provider 也得重配。
常见错误现象:Call to undefined method IlluminateAuthRequestGuard::login()(想调 login() 却没实现)、Token has expired(没配 exp 或没刷新)、Unauthenticated(中间件没识别 Bearer 头)。
- 别用
php artisan make:auth—— 它只配 Session,和 JWT 冲突 - 推荐用
tymon/jwt-auth(v2.x 支持 Laravel 9+),但注意它已停止维护;更稳妥选laravel/sanctum(适合 API + SPA)或纯手写firebase/php-jwt(轻量、可控) - 如果坚持用
tymon/jwt-auth,必须运行php artisan vendor:publish --provider="TymonJWTAuthProvidersLaravelServiceProvider"并手动改config/jwt.php中的secret(别用默认值!)
如何让 login 接口返回 JWT 而不是跳转或 Session
默认 Auth::attempt() 只返回 bool,JWT 需要显式签发 token。关键不是“怎么登录”,而是“登录成功后怎么生成、返回、存 token”。
使用场景:APP 或前端调 /api/login,期望响应体含 {"token": "xxx"},后续请求带 Authorization: Bearer xxx。
- 别在控制器里直接写
JWTAuth::fromUser($user)—— 如果没提前配置好 guard,会抛JWTException - 先确保
config/auth.php中'defaults' => ['guard' => 'api'],且'guards'['api']['driver']设为jwt(用 tymon)或sanctum(用 sanctum) - login 方法示例(tymon):
use TymonJWTAuthFacadesJWTAuth; public function login(Request $request) { $credentials = $request->only('email', 'password'); if (!$token = JWTAuth::attempt($credentials)) { return response()->json(['error' => 'Unauthorized'], 401); } return response()->json(compact('token')); } - 注意:tymon v2.x 默认不支持 refresh,如需自动续期,得加
refresh接口并设ttl和refresh_ttl(单位分钟)
为什么 Auth::user() 在 API 路由里总是 null
因为 Laravel 默认的 web guard 依赖 session cookie,而 API 请求通常没 cookie,也不走 session 启动流程。哪怕 token 正确,guard 没被正确调用,Auth::user() 就是 null。
参数差异:Session guard 查 session_id(),JWT guard 查 Authorization header 里的 Bearer token 并解码验证签名。
- 确认路由用了
api中间件组:Route::middleware('api')->group(...),而不是web - 检查
app/http/Kernel.php中'api'中间件组是否包含IlluminateSessionMiddlewareStartSession::class—— API 路由里必须删掉它,否则 session 启动会干扰 JWT 解析 - 若用 sanctum,确保模型用了
HasApiTokenstrait,且config/sanctum.php的stateful域名不含你的 API 前端(比如localhost:5173不该在 stateful 列表里) - 调试技巧:在中间件里 dump
request()->header('Authorization')和JWTAuth::parseToken()->authenticate()看哪步失败
token 存哪?前端怎么传?过期了怎么办
JWT 是客户端存储的凭证,服务端只校验,不保存。存错地方或传错格式,立刻 401。
性能影响:每次请求都 decode + verify signature + 检查 exp,比 session id 查 redis 略重,但可接受;兼容性上,IE 不支持 fetch 的 headers 设置,老项目得用 XMLHttpRequest。
- 前端存 token 推荐
localStorage(方便调试),但 xss 风险高;更安全用httpOnlycookie(需后端 set-cookie,且跨域要配withCredentials: true) - 传 token 必须是
Authorization: Bearer <token></token>,少一个空格、多一个引号、漏Bearer前缀都会失败 —— 后端解析时request()->bearerToken()就拿不到 - 过期处理不能只靠前端倒计时:服务端要校验
exp字段,且建议加nbf(not before)防重放;刷新 token 时,旧 token 应加入黑名单(tymon 提供invalidate(),但需自己存黑名单位置) - 别把 refresh token 和 access token 存一起 —— refresh token 要更长 ttl、更严校验(比如绑定 user agent / IP)
最常被忽略的是:JWT 的 sub(subject)字段默认是用户 ID,但如果用户被软删除或禁用,JWTAuth::parseToken()->toUser() 仍会返回模型实例。得在 User 模型里重写 getJWTIdentifier() 或加中间件做二次校验。