axios 为什么我得到错误“CSRF令牌不匹配”,使用Laravel 10 API的React 18 SPA

w6lpcovy  于 2023-10-18  发布在  iOS
关注(0)|答案(2)|浏览(113)

我正在使用连接到Laravel 10 API的React 18构建一个SPA,其中已经包括用于身份验证的Sanctum,并且我安装了Breeze API来生成相应的脚手架。
我已经遵循了官方Laravel文档,但因为它不足以解决这个问题,我开始与谷歌研究其他开发人员做了什么来摆脱这个不便。我读过许多不同的修复方法,几乎都试过了(其中一些根本没有意义,或者不推荐),直到我在github上看到一个老帖子,由泰勒奥特韦尔自己回答,当时圣所被称为气闸:

因此,我在WAMP中创建了一个域和一个子域,以测试这个可能的解决方案,但仍然看到相同的错误弹出令人沮丧:

正如大家所看到的,GET请求没有问题,但是POST请求有问题。对于这个项目,我安装了AXIOS,但我注意到在发布数据时cookie没有被发送回API服务器。
axios.js

import axios from "axios";

export default axios.create({
   baseURL: 'http://api.localdomain',
   headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json'
   },
   withCredentials: true
})

这是我的.env文件:

APP_NAME=localdomain
APP_ENV=local
APP_KEY=base64:SjhdnO/kZ2IMWGKEdgrw+a5e1eToxvtLHsbJvwf0dAs=
APP_DEBUG=true
APP_URL=http://api.localdomain
FRONTEND_URL=http://localdomain

SESSION_DRIVER=cookie
SESSION_LIFETIME=120

MEMCACHED_HOST=127.0.0.1

SESSION_DOMAIN=.localdomain
SANCTUM_STATEFUL_DOMAINS=api.localdomain

config/cors.php

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

   'allowed_methods' => ['*'],
   'allowed_origins' => [env('FRONTEND_URL')],
   'allowed_origins_patterns' => [],
   'allowed_headers' => ['*'],
   'exposed_headers' => [],
   'max_age' => 0,
   'supports_credentials' => true,

config/session.php

'domain' => env('SESSION_DOMAIN'),
'http_only' => true,
'same_site' => 'lax',

config/sanctum.php

'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf('%s%s%s',
       'api.localdomain,localdomain,127.0.0.1,127.0.0.1:8000,::1',
        env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : '',
        env('FRONTEND_URL') ? ','.parse_url(env('FRONTEND_URL'), PHP_URL_HOST) : ''
    ))),

    'middleware' => [
        'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
        'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
    ],

routes/auth.php

<?php

use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Auth\NewPasswordController;
use App\Http\Controllers\Auth\PasswordResetLinkController;
use App\Http\Controllers\Auth\RegisteredUserController;
use Illuminate\Support\Facades\Route;

Route::post('/register', [RegisteredUserController::class, 'store'])->middleware('guest');

Route::post('/login', [AuthenticatedSessionController::class, 'store'])->middleware('guest');
Route::post('/logout', [AuthenticatedSessionController::class, 'destroy'])->middleware('auth');

Route::post('/forgot-password', [PasswordResetLinkController::class, 'store'])->middleware('guest');

Route::post('/reset-password', [NewPasswordController::class, 'store'])->middleware('guest');

这些路由是使用以下命令生成的路由:

# Install Breeze and dependencies...
composer require laravel/breeze --dev

php artisan breeze:install api

我已经尝试了.env文件cors.php和sanctum.php中可能值的许多不同组合,但到目前为止没有任何积极的结果。任何帮助如何使这项工作是真正的赞赏,因为我已经花了3天的时间尝试不同的修复无济于事。

编辑:(2023/08/19)

看看Axios如何确定是否设置响应头'X-XSRF-TOKEN',我知道POST请求(/login route)头值来自document.cookies字符串。然而,文档. cookie不是由Laravel(或其软件包之一)设置的,尽管我改变了:

'domain' => '.localdomain',
'secure' => false,
'http_only' => false,
'same_site' => 'lax'

values in php/session.php
我已经在我的开发环境和我的本地域中测试过了。结果相同(状态代码419 / CSRF令牌不匹配)。
在后端端仍然缺少一些东西或配置错误。

vc9ivgsu

vc9ivgsu1#

好吧,经过大量的研究和测试,我终于得出了许多人可能正在寻找的答案:使用由Sanctum(Laravel 10)认证的ReactJS SPA,而无需对Laravel配置进行太多更改。
事实上,当你重新安装Laravel时,它已经包含了Sanctum包,所以你需要做的就是安装Breeze API,以创建路由和身份验证脚手架。整个安装过程如下:

  1. Laravel/laravel my-api
  2. cd my-api
  3. composer require laravel/breeze --dev
  4. php artisan breeze:安装API
    1.运行迁移。
    就是这样。你不需要在配置文件和原始路由中做任何改变,除非你确切地知道你想做什么。
    当然,唯一需要的更改是在.env文件中。除此之外,不需要做任何其他事情。
    下面是我如何配置.env文件的例子:
APP_NAME=localdomain
APP_ENV=local
APP_KEY=base64:bla bla bla=
APP_DEBUG=true
APP_URL=https://api.localdomain.dom
FRONTEND_URL=https://localdomain.dom

SESSION_DOMAIN=.localdomain.dom
SESSION_DRIVER=cookie
SESSION_LIFETIME=120

通过阅读config/session.php文件中的注解,我意识到应该(在两端)使用HTTPS协议,以便将会话cookie发送回服务器(作为头)。
当然,如果我们要发送一个安全的cookie进行身份验证,那么使用HTTPS协议是有意义的。如果没有,会话cookie不会被发送回服务器(在POST请求中),这就是为什么我们得到419错误。
为了让它正常工作,我使用了Mkcert软件来创建我的证书。如何创建本地信任的SSL证书
这个软件让我省去了很多麻烦。
一旦创建了具有各自证书的虚拟主机,我的SPA就可以正常运行了。同样,泰勒奥特韦尔是正确的,当他说,这工作,一个真实的域和子域是必要的。无法使用Localhost,因为它不是真实的主机。
我希望这可以帮助其他人,因为有很多奇怪的修复和神话,真的很少或没有什么关系运行一个与Laravel Sanctum正确的SPA。

hzbexzde

hzbexzde2#

编辑获取CSRF令牌并在cookie中设置

axios.get('/sanctum/csrf-cookie').then(response => {
    // Make other requests here and set cookie
});

在发出登录的POST请求时,确保X-XSRF-TOKEN标头使用cookie中的CSRF令牌进行设置。Axios在使用CSRF令牌cookie时自动包含此标头。

**更新:**还需要在resources/js/bootstrap.js中设置withCredentials

axios.defaults.withCredentials = true;

相关问题