You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 5.6中无会话存储JWT至Cookie的实现问题

解决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_session Cookie,说明某个路由或中间件还在触发会话,仔细检查一遍你的路由组和中间件配置。

内容的提问来源于stack exchange,提问作者lorenzo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:00:54