#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:
- Одна особенность, которую я абсолютно не понимаю, почему я работаю с веб-промежуточным интерфейсом в маршруте 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 должен это понять, если вы не установите его вручную