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

Laravel中Axios POST请求出现CSRF Token不匹配问题求助

React + Laravel Sanctum SPA认证419 CSRF Token不匹配问题

我在用React前端(localhost:3000)和Laravel后端(localhost:8888)开发项目,采用Sanctum实现SPA认证。已成功通过sanctum/csrf-cookie路由获取XSRF-Token Cookie,但发起登录POST请求时出现419错误,提示CSRF Token不匹配且无XSRF-Token。向testing路由发送GET请求时Cookie存在,POST登录请求则无Cookie并报错。

问题排查与修复步骤

1. 调整登录路由的中间件分组

你的/login路由定义在web.php中,默认走web中间件组,而SPA认证的登录接口应放在api中间件组下,避免VerifyCsrfToken直接拦截。

修改web.php中的登录路由,添加api中间件:

Route::post('/login', function(Request $request) {
    // 原有逻辑不变
})->middleware('api');

2. 修正SESSION_DOMAIN配置

本地环境下,SESSION_DOMAIN设为localhost会存在端口隔离问题,改为.localhost(注意前缀的点):

SESSION_DOMAIN=.localhost

这样不同端口的localhost域名可共享Cookie。

3. 显式配置axios的XSRF参数

虽然已设置withCredentials: true,但显式指定XSRF的Header和Cookie名称可避免默认值不一致问题,在Bootstrap.js中添加:

axios.defaults.xsrfHeaderName = 'X-XSRF-TOKEN';
axios.defaults.xsrfCookieName = 'XSRF-TOKEN';

Laravel Sanctum默认使用X-XSRF-TOKEN作为Header名,XSRF-TOKEN作为Cookie名。

4. 清除浏览器缓存与Cookie

旧Cookie可能导致干扰,清空浏览器中localhost相关的Cookie,重启前后端服务后重新测试。

相关代码片段

LoginForm.js

let data = {
  email: e.target[0].value,
  password: e.target[1].value
}
axios.get('http://localhost:8888/sanctum/csrf-cookie')
.then((res) => {
  axios.post('http://localhost:8888/login', data)
  .then((res) => {
    axios.get('http://localhost:8888/user')
  })
})

Kernel.php

protected $middleware = [
        \App\Http\Middleware\TrustProxies::class,
        \Fruitcake\Cors\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
    ];

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            \App\Http\Middleware\HandleInertiaRequests::class,
        ],

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];

.env

SESSION_DRIVER=cookie
CLIENT_URL=http://localhost:3000
SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=http://localhost:3000

Bootstrap.js

axios = require('axios');

axios.defaults.headers.common['Accept'] = 'application/json';
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
axios.defaults.withCredentials = true;

Web.php

Route::get('/testing', function () {
    return "Testing.";
});

Route::post('/login', function(Request $request) {
    $credentials = $request->validate([
        'email' => ['required', 'email'],
        'password' => ['required'],
    ]);

    if (Auth::attempt($credentials)) {
        $request->session()->regenerate();
        $id = Auth::id();
        $user = User::find($id);
        return $user;
    }

    return back()->withErrors([
        'email' => 'The provided credentials do not match our records.',
    ]);
});

Sanctum.php

'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
    '%s%s',
    'localhost,localhost:3000,localhost:8888,
    Sanctum::currentApplicationUrlWithPort()
))),

Cors.php

'paths' => [
        'api/*',
        'sanctum/csrf-cookie',
        'login',
        'logout',
        'register',
        'user/password',
        'forgot-password',
        'reset-password',
        'user/profile-information',
        'email/verification-notification',
        'testing',
        'user',
        'checkAuth'
    ],

'allowed_methods' => ['*'],

'allowed_origins' => [env('CLIENT_URL')],

'allowed_origins_patterns' => [],

'allowed_headers' => ['*'],

'exposed_headers' => [],

'max_age' => 0,

'supports_credentials' => true,

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 17:10:24