Только начинаем с barryvdh/laravel-cors
. У меня есть приложение на стороне сервера, работающее на порту 8000, которое аутентифицирует пользователей через Laravel Passport, и мое клиентское приложение react.js, работающее на порту 3000. Сейчас я пытаюсь заставить работать действие «вход».
От клиента, когда я предоставляю правильные учетные данные, токен отправляется обратно, и статус равен 200. Если учетные данные недействительны, возвращается 401. Так хорошо до сих пор. Однако, если я укажу только адрес электронной почты в форме и не укажу пароль, я получу следующую ошибку, связанную с CORS:
При возникновении ошибки происходят следующие сетевые вызовы:
Мой логин на стороне клиента:
login = (email, password) => {
console.info(email);
fetch("http://localhost:8000/api/auth/login", {
method: 'POST',
body: JSON.stringify({
'email': email,
'password': password
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then(response => {
console.info(response['access_token']); //with correct credentials this is logged out and set in local storage
localStorage.setItem('access_token', response['access_token']);
})
.catch(error => console.error('Error: ', error));
}
Сторона сервера:
public function login(Request $request)
{
$request->validate([
'email' => 'required|string|email',
'password' => 'required|string',
'remember_me' => 'boolean'
]);
$credentials = request(['email', 'password']);
if (!Auth::attempt($credentials)) {
return response()->json([
'message' => 'Unauthorized'
], 401);
}
$user = $request->user();
$tokenResult = $user->createToken('Personal Access Token');
$token = $tokenResult->token;
if ($request->remember_me) {
$token->expires_at = Carbon::now()->addWeeks(1);
}
$token->save();
return response()->json([
'access_token' => $tokenResult->accessToken,
'token_type' => 'Bearer',
'expires_at' => Carbon::parse(
$tokenResult->token->expires_at
)->toDateTimeString()
]);
}
cors.php
(опубликовано из пакета barryvdh/laravel-cors):
return [
'supportsCredentials' => false,
'allowedOrigins' => ['*'],
'allowedOriginsPatterns' => [],
'allowedHeaders' => ['*'],
'allowedMethods' => ['*'],
'exposedHeaders' => [],
'maxAge' => 0,
];
api.php
:
Route::group(['prefix' => 'auth',], function () {
Route::post('login', 'AuthController@login');
Route::post('register', 'AuthController@register');
Route::get('register/activate/{token}', 'AuthController@registerActivate');
Route::group(['middleware' => 'auth:api'], function() {
Route::get('logout', 'AuthController@logout');
Route::get('user', 'AuthController@user');
});
});
Kernel.php
:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
\Barryvdh\Cors\HandleCors::class,
'throttle:60,1',
'bindings',
],
];
protected $middleware = [
\Barryvdh\Cors\HandleCors::class,
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\TrustProxies::class
];
Выполняя тот же запрос POST (один к /api/auth/login
с исключенным паролем) через почтальона, я получаю ожидаемое сообщение об ошибке:
{
"message": "The given data was invalid.",
"errors": {
"password": [
"The password field is required."
]
}
}
Но, делая это через клиентское приложение реакции, выдается ошибка на скриншоте. Я предполагаю, что это как-то связано с предварительным HTTP-запросом OPTIONS, который браузер отправляет как часть работы CORS.
Как я могу это исправить? Я ожидаю, что для статуса будет возвращено 401, а реагирующий клиент получит то же сообщение JSON, которое Почтальон получает в случае, если пароль пуст. Просто кажется странным, что на основе полезной нагрузки для этого запроса POST возникает ошибка, связанная с CORS. Означает ли это, что сервер и/или клиент неправильно настраивают CORS?
Я думал, что только на стороне сервера нужно установить заголовки, связанные с CORS. Какие заголовки нужно установить на стороне клиента? Я не думаю, что смогу избежать перенаправления CORS
Реагирующее приложение, похоже, обслуживается с другого сервера, где есть localhost: 3000 и localhost: 8000, к которым клиент обращается, поэтому им обоим нужны заголовки CORS.
можете ли вы добавить свою конфигурацию CORS для бэкэнда в вопросе?
Добавлен. Я должен был опубликовать это раньше - я не совсем уверен, что правильно настроил эту часть. Я думаю, что у меня есть просто настройки конфигурации по умолчанию из пакета barryvdh/laravel-cors
вы выполнили команду php artisan vendor:publish --provider = "Barryvdh\Cors\ServiceProvider"
?
да. вот откуда взялся cors.php
на данный момент кажется отличным, не могли бы вы также добавить config/app.php
контент и раздел промежуточного программного обеспечения в свой Kernel.php
?
Ничего не изменилось в app.php
относительно пакета cors, должны ли там быть изменения? выложу kernel.php
могу я спросить, зачем нужны разные порты?
Я использую разные порты для разработки, поскольку, когда я фактически развертываю это приложение в целом, API Laravel будет обслуживаться веб-сервером Nginx, а приложение React будет обслуживаться через node. По сути, я пытаюсь сохранить полное разделение между клиентом и сервером — надеюсь, это имеет смысл.
@Ronald, просто чтобы уточнить: используются разные порты, потому что локально у меня работают 2 сервера. Один обслуживает php-приложение, а другой обслуживает реагирующее приложение.
я вижу, что \Barryvdh\Cors\HandleCors::class,
объявлен как в $middleware
, так и в $middlewareGroups
, можете ли вы попробовать удалить его из раздела $middlewareGroups
, и еще одна вещь, я заметил, что у многих людей есть эта проблема, и она, похоже, связана с версией пакета, какая версия ты используешь ? Взгляните на эти проблемы: ссылка1, ссылка2
Я попытался удалить запись в $middlewareGroups
- похоже, это не имеет значения. Я использую последнюю версию (0.11.3). Я думаю, что нашел проблему - на стороне сервера мне требуется пароль (при вызове $request->validate(...)
), когда эта проверка не проходит (из-за того, что клиент не предоставляет пароль), кажется, что промежуточное программное обеспечение CORS не полностью работает - это тут же проваливается. Изменение этого поля на необязательное на стороне сервера (что, конечно, не очень хорошая идея) приводит к возврату 401, когда пароль не указан (поэтому неверная комбинация пользователя/пароля)
@AbdelkarimELAMEL, вы должны поместить свои комментарии в качестве ответа, в частности, вторую ссылку, которую вы предоставили, и информацию github о том, что «при возникновении ошибки промежуточное программное обеспечение не запускается полностью». В этом была проблема. Я просто не видел этого в репо и не ожидал ошибки, связанной с CORS. Спасибо за вашу помощь
Пожалуйста, я добавлю это как ответ.
Прежде всего член класса:
protected $middleware
Применит промежуточное ПО как для api
, так и для web
Отlaravel.com/docs/5.8/middleware#registering-middleware
If you want a middleware to run during every HTTP request to your application, list the middleware class in the $middleware property of your app/Http/Kernel.php class.
Смотрите также:barryvdh/laravel-cors#глобальное использование
Поэтому попробуйте зарегистрировать свое промежуточное ПО один раз, удалив:
\Barryvdh\Cors\HandleCors::class
из ваших 'api'
группа промежуточного программного обеспечения.
И для логических целей рефакторинг вашего protected $middleware
с помощью:
protected $middleware = [
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\TrustProxies::class,
\Barryvdh\Cors\HandleCors::class,
];
И приложение laravel, и приложение react должны правильно отправлять заголовки CORS. Если вы можете избежать перенаправлений CORS, с этим будет намного проще справляться.