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
相关产品推荐
相关产品推荐

