Laravel 5.6中无会话存储JWT至Cookie的实现问题
兄弟,这个问题我在Laravel 5.6那会儿折腾过好一阵!想靠Cookie存JWT彻底抛弃会话,确实得把Laravel默认的会话机制连根拔了才行,我给你捋捋一步步怎么搞:
1. 彻底清理Web中间件组里的会话相关组件
Laravel的Web中间件组默认带了一堆会话相关的东西,这就是为啥你删了部分还会启动会话的原因。打开app/Http/Kernel.php,把Web组里所有和会话、会话共享相关的中间件都删掉,只留必要的:
protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, // 如果你的Web页面需要CSRF保护就保留下面这行,不需要就删掉 // \App\Http\Middleware\VerifyCsrfToken::class, ], // ... 其他中间件组不动 ];
注意:ShareErrorsFromSession和StartSession、AuthenticateSession必须全删,前一个依赖会话,留着会直接报错。
2. 配置Auth系统用JWT Guard
Laravel默认用的是session guard,得改成JWT驱动的guard。打开config/auth.php:
// 首先定义JWT驱动的guard 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'jwt', // 这里指定用JWT驱动 'provider' => 'users', ], ], // 把默认Guard改成api(也就是我们上面定义的JWT guard) 'defaults' => [ 'guard' => 'api', 'passwords' => 'users', ],
这样之后,auth()助手函数默认就用JWT guard了,不用每次都写auth('api')。
3. 完善登录时的Cookie存储逻辑
你已经在Auth控制器返回Cookie了,但要注意Cookie的属性配置,别不小心弄成会话Cookie了。登录方法可以这么写:
public function login(Request $request) { $credentials = $request->only('email', 'password'); // 用JWT guard验证 credentials if (! $token = auth()->attempt($credentials)) { return response()->json(['error' => 'Unauthorized'], 401); } // 把JWT存入持久化Cookie,注意属性设置 return response()->json(['message' => '登录成功']) ->cookie( 'jwt_token', // Cookie名 $token, 60 * 24, // 有效期1天(分钟) '/', // 生效路径 env('APP_DOMAIN'), // 你的应用域名 env('APP_ENV') === 'production', // 生产环境开启Secure(HTTPS下生效) true, // HttpOnly,防止XSS窃取 false, // 关键!设为false表示是持久Cookie,不是会话Cookie 'Lax' // SameSite属性,提升安全性 ); }
4. 写个中间件从Cookie取JWT令牌
默认的JWT中间件是从Authorization头拿令牌的,我们得自己写个中间件从Cookie里取,然后传给JWT验证逻辑。创建app/Http/Middleware/JwtFromCookie.php:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Tymon\JWTAuth\Facades\JWTAuth; class JwtFromCookie { public function handle(Request $request, Closure $next) { // 检查请求里有没有JWT Cookie if ($request->hasCookie('jwt_token')) { $token = $request->cookie('jwt_token'); // 把令牌放到Authorization头里,让默认的JWT中间件能识别 $request->headers->set('Authorization', 'Bearer ' . $token); // 提前验证令牌有效性,无效就清Cookie返回未授权 try { JWTAuth::parseToken()->authenticate(); } catch (\Exception $e) { return response()->json(['error' => '令牌无效'], 401)->withoutCookie('jwt_token'); } } return $next($request); } }
然后在Kernel.php的路由中间件里注册它:
protected $routeMiddleware = [ // ... 其他中间件 'jwt.cookie' => \App\Http\Middleware\JwtFromCookie::class, ];
之后在需要验证的路由上用这个中间件:
Route::group(['middleware' => ['jwt.cookie']], function () { Route::get('/dashboard', 'DashboardController@index'); // 其他需要登录才能访问的路由 });
5. 登出逻辑要清除Cookie而非销毁会话
因为我们不用会话,登出时只要清除JWT Cookie就行:
public function logout(Request $request) { try { auth()->logout(); } catch (\Exception $e) { // 就算令牌已经失效,也直接清Cookie就行 } return response()->json(['message' => '登出成功']) ->withoutCookie('jwt_token'); }
避坑提醒
- 绝对不要在代码里调用
session()、request()->session()或者Session门面,不然会直接报错,因为会话已经被禁用了。 - 页面要显示用户信息的话,用
auth()->user()就行,因为默认guard已经改成JWT了。 - 测试的时候看响应头,如果还有
laravel_sessionCookie,说明某个路由或中间件还在触发会话,仔细检查一遍你的路由组和中间件配置。
内容的提问来源于stack exchange,提问作者lorenzo
相关产品推荐
相关产品推荐

