Получите маркер CSRF-несоответствие — Vue axios по маршруту Laravel Api

#php #laravel #vue.js #axios #session-cookies

Вопрос:

Я пытаюсь сохранить корзину покупок клиента в сеансе на стороне сервера. Клиенту не нужно входить в систему. Так что ни Святилища Ларавеля, ни паспорта и т. Д. Laravel и Vue работают на разных серверах (Серверная часть: локальный хост:8000 и внешний интерфейс: локальный хост:9000).

Вот мои api.php маршруты.

 Route::get('get-token', function (Request $request) {
    return [
        '_token' => $request->session()->token()
    ];    
})->middleware('web');

Route::group(['prefix'=>'v1'], function() {
    Route::resource('cart', AppHttpControllersApiV1CartController::class)->middleware('web');
    Route::resource('products', AppHttpControllersApiV1ProductController::class);
}
 

Запросы на получение /корзину все работают нормально через axios и insomnia:

вот индекс функций картконтроллера corospond:

 function index() {
    return request()->session()->get('cart');
}
 

Теперь проблема

Но запрос на публикацию через axios отклоняется с пропуском 419 токенов CSRF.

Как ни странно, я могу отправить запрос по ПОЧТЕ через Бессонницу. Сначала запросив токен с сервера, а затем указав _token в теле запроса POST.

 > POST /api/v1/cart HTTP/1.1
> Host: localhost:8000
> User-Agent: insomnia/2021.5.3
> Cookie: laravel_session=ZzbpJsfvV22NjO3TB5C3ekiCIp7hlPAcaaZHTkfU; XSRF-TOKEN=eyJpdiI6IjhvVFpWMWtwMm0vU1RKNHB2VzdMdUE9PSIsInZhbHVlIjoiaDNEWTlhcTBRTWI0aXhkaU54Z3I5VndoWFg4Q1A1WmpJUDI1S2hFRGtiMUUwYUl0OEN6S0Q3RVFDZDJXSTNvdTllUXMzN0o1RU94blpqVTdpdTcxaTVvZjlwdFk2Z3djeldteHp0R3Q4UEVjMEovbGk2SHJnVXZmbzRBUzI1RHkiLCJtYWMiOiI2MDMyYzFjNWVjODY4NjRjMTk4NGNmMmI3Yjg4M2VjYzU5YzcwZGRkNDIxMTRiOTc1N2FkMmQ2NTc4YTA5MjhiIiwidGFnIjoiIn0=
> Content-Type: multipart/form-data; boundary=X-INSOMNIA-BOUNDARY
> Accept: application/json
> Content-Length: 293

| --X-INSOMNIA-BOUNDARY
| Content-Disposition: form-data; name="product_id"
| 1
| --X-INSOMNIA-BOUNDARY
| Content-Disposition: form-data; name="amount"
| 1
| --X-INSOMNIA-BOUNDARY
| Content-Disposition: form-data; name="_token"
| YgKRkQc9D5GWOPiP8zqVEcoA4FuvESf02SSjy7U3
| --X-INSOMNIA-BOUNDARY--
 

Одна особенность, которую я абсолютно не понимаю, почему я работаю с веб-промежуточным интерфейсом в маршруте api. Промежуточное программное обеспечение api сгенерирует для меня это сообщение об ошибке 500:

"message": "Session store not set on request.",

И вот моя просьба к аксиосу:

         axios.get('http://localhost:8000/api/get-token').then(response => {
            const _token = response.data._token
            const bodyFormData = new FormData()
            bodyFormData.append('product_id', product.id)
            bodyFormData.append('_token', _token)
            const headers = {
              'Content-Type': 'multipart/form-data',
              'Access-Control-Allow-Credentials': true,
              '_token': _token             
            }

            axios({
              method: "post",
              url: 'cart',
              data: bodyFormData,
              headers: headers,
            }).then((res) => {
              console.log('res',res)
            }).catch((err) => {console.log(err)})

        }); 
 

Это действительно длинный текст. Надеюсь, я предоставил всю необходимую информацию.

Ответ №1:

  1. Одна особенность, которую я абсолютно не понимаю, почему я работаю с веб-промежуточным интерфейсом в маршруте api.
  • Это связано с тем, что маршруты API не поддерживают сеансы для приложения, поскольку оно ожидает, что приложение будет использовать токены, поэтому для использования сеансов вам необходимо «веб -» промежуточное программное обеспечение

Ошибка несоответствия токенов может быть вызвана тем, что сгенерированный/предоставленный токен через конечную точку get-token не будет правильным

  • Решением, которое я вижу, было бы добавить маршрут /корзина в качестве маршрута исключения для CSRFTokenMatch, для этого откройте приложение/Http/Промежуточное программное обеспечение/VerifyCsrfToken.php-файл и добавьте маршрут в защищенный массив $except. Это не идеальный вариант, но на данный момент это решит вашу проблему и поможет вам двигаться вперед.

Еще один вариант обеспечения безопасности вашего приложения-использовать Laravel Sanctum и генерировать токен для каждого гостя (используя гостевую модель), если вам нужна помощь в этом, я могу объяснить подробнее, но это немного другой сценарий

Комментарии:

1. Равель Спасибо за первое быстрое исправление, я поместил маршрут api/v1/cart в массив $exept. И это работает так, что я могу отправлять запросы на сервер. Моя проблема в том, что он воспринимает каждый другой запрос как новый. Это означает, что у меня нет уникальной корзины покупок, но много сеансов. Когда я перезагружаю страницу, в корзине ничего нет. У вас есть еще одно быстрое решение 😉 ?

2. Да, это, конечно, несколько сложно с сессией, но позвольте мне дать вам несколько советов, хотя я не уверен, сработает это или нет.

3. 1. Вы берете идентификатор сеанса из своего запроса, используя запрос()->сеанс()->>getId (), а затем сохраняете этот сеанс в своем приложении vue, используя localStorage.setItem(‘session_id’, res.data.SessionID)

4. 2. отправьте идентификатор сеанса в своем запросе axios post, используя bodyFormData.append(«идентификатор сеанса», localStorage.GetItem(«идентификатор сеанса»))

5. 3. теперь на стороне Laravel попробуйте повторно инициализировать сеанс по запросу()->сеанс()->>setId(запрос(‘session_id’))

Ответ №2:

Мое первое предположение было бы просто, что вы не отправляете файлы cookie, и вам нужно это сделать

 axios.defaults.withCredentials = true
 

или более безопасный (поскольку он ограничивает withCredentials время, когда вы действительно хотите его использовать):

 const axiosWithCredentials = axios.create({
     withCredentials: true
});
axiosWithCredentials.get('http://localhost:8000/api/get-token').then(response => {
        const _token = response.data._token
        const bodyFormData = new FormData()
        bodyFormData.append('product_id', product.id)
        bodyFormData.append('_token', _token)
        const headers = {
          'Content-Type': 'multipart/form-data',
          'Access-Control-Allow-Credentials': true,
          '_token': _token             
        }

        axiosWithCredentials({
          method: "post",
          url: 'cart',
          data: bodyFormData,
          headers: headers,
        }).then((res) => {
          console.log('res',res)
        }).catch((err) => {console.log(err)})

    }); 
 

Комментарии:

1. Спасибо, Дэйв. Все тот же ответ HTTP Статус = 419

2. Хорошо, заглядывая дальше, попробуйте вообще не устанавливать заголовок типа содержимого, и пусть axios разберется-все составные типы имеют общий синтаксис и включают параметр границы как часть значения типа носителя, которого у вас нет. Но в любом случае вам это здесь не нужно, axios должен это понять, если вы не установите его вручную