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

Nuxt3+Laravel12 Sanctum SPA认证:每次请求生成新会话问题

问题分析与解决方案

核心问题

你的场景下会话ID每次请求都重置,本质是浏览器未在跨端口请求中正确携带会话Cookie,导致Laravel无法识别已有会话,每次创建新会话,最终引发CSRF令牌不匹配。


疑问解答

1. localhost端口与Cookie的特性

浏览器把同一域名下的不同端口视为不同源,但Cookie的domain设为.localhost时,所有localhost:*端口都能共享该Cookie。不过部分浏览器(如Chrome)对localhost的Cookie有特殊处理,需确保Cookie的path为/,且same_site设置不阻止跨端口请求。

2. 会话ID每次变化的原因

大概率是以下某一点导致浏览器未携带会话Cookie:

  • CORS配置冲突,浏览器拒绝存储/发送Cookie
  • 路由中间件顺序错误,禁用了会话功能
  • Axios未全局开启withCredentials
  • 会话Cookie的http_only/same_site属性限制了跨端口传输

3. 跨端口能否实现Laravel会话认证

完全可以。只要配置正确,确保Cookie能跨端口共享、请求携带credentials,Laravel就能通过database驱动维持跨请求的会话状态。


具体修复步骤

1. 清理重复CORS配置

你同时配置了官方config/cors和自定义CorsMiddleware,会导致响应头重复,浏览器可能拒绝处理。直接删除自定义的CorsMiddleware,保留官方配置:

// config/cors.php
return [
    'paths' => ['api/*', 'sanctum/csrf-cookie', 'oauth/*'],
    'supports_credentials' => true,
    'allowed_methods' => ['*'],
    'allowed_origins' => ['http://localhost:3000'],
    'allowed_headers' => ['*'],
    'exposed_headers' => ['XSRF-TOKEN', 'Authorization'],
    'max_age' => 0,
];

2. 修正会话Cookie配置

确保config/session.php的关键属性正确:

return [
    'driver' => env('SESSION_DRIVER', 'database'),
    'cookie' => env('SESSION_COOKIE', Str::slug(env('APP_NAME', 'laravel'), '_').'_session'),
    'path' => '/',
    'domain' => '.localhost', // 带前置点,允许所有localhost端口共享
    'secure' => false, // 本地HTTP环境设为false
    'http_only' => true, // 安全设置,不影响XSRF-TOKEN的获取
    'same_site' => 'lax', // 跨端口请求用lax足够
];

3. 修正路由中间件顺序

api中间件会默认禁用会话,不能把状态化路由嵌套在api中间件组里。修改routes/api.php:

Route::prefix('v1')->group(function () {
    // 公开路由(无需会话)
    Route::post('/register', [AuthController::class, 'CustomerRegister']);

    // 状态化路由(登录+受保护接口)
    Route::middleware([EnsureFrontendRequestsAreStateful::class])->group(function () {
        Route::post('/login', [AuthController::class, 'AdminAffiliateLogin']);
        Route::post('/logincustomer', [AuthController::class, 'CustomerLogin']);

        Route::middleware('auth:sanctum')->group(function () {
            Route::post('/logout', [AuthController::class, 'logout']);
            Route::get('/getallbrochure', [BrochureController::class, 'index']);
            Route::get('/getbrochure/{id}', [BrochureController::class, 'show']);
            Route::post('/uploadbrochure', [BrochureController::class, 'store']);
            Route::get('/deletebrochure/{id}', [BrochureController::class, 'destroy']);
            Route::put('/updatebrochure/{id}', [BrochureController::class, 'update']);
        });
    });
});

4. 统一Axios配置,确保全局携带Credentials

在Nuxt 3的plugins/axios.ts中全局配置,避免多实例遗漏:

import axios from 'axios'

export default defineNuxtPlugin(() => {
  const apiBaseUrl = 'http://localhost:8000/api/v1'
  const axiosInstance = axios.create({
    baseURL: apiBaseUrl,
    withCredentials: true,
  })

  // 请求前自动获取CSRF令牌(针对修改类请求)
  axiosInstance.interceptors.request.use(async (config) => {
    if (['post', 'put', 'delete'].includes(config.method?.toLowerCase() || '')) {
      await axios.get('http://localhost:8000/sanctum/csrf-cookie', { withCredentials: true })
      const xsrfToken = useCookie('XSRF-TOKEN').value
      if (xsrfToken) config.headers['X-XSRF-TOKEN'] = xsrfToken
    }
    return config
  })

  return { provide: { axios: axiosInstance } }
})

5. 验证会话Cookie状态

打开浏览器F12开发者工具:

  • 到Application标签,确认laravel_session(或自定义会话Cookie名)存在,domain为.localhost、path为/
  • 到Network标签,查看后续请求的请求头是否包含Cookie: laravel_session=xxx; XSRF-TOKEN=xxx

6. 确保会话表正常

如果之前未生成会话表,执行:

php artisan session:table
php artisan migrate

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 20:28:12