Tôi đã gặp lỗi này với Laravel Sanctum. Tôi đã sửa nó bằng cách thêm \Illuminate\Session\Middleware\StartSession::class,
vào api
nhóm phần mềm trung gian trong Kernel.php, nhưng sau đó tôi phát hiện ra điều này "hoạt động" vì các tuyến xác thực của tôi đã được thêm vào api.php
thay vì web.php
, vì vậy Laravel đã sử dụng sai bảo vệ xác thực.
Tôi đã chuyển các tuyến đường này vào đây web.php
và sau đó chúng bắt đầu hoạt động bình thường với AuthenticatesUsers.php
đặc điểm:
Route::group(['middleware' => ['guest', 'throttle:10,5']], function () {
Route::post('register', 'Auth\RegisterController@register')->name('register');
Route::post('login', 'Auth\LoginController@login')->name('login');
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');
Route::post('password/reset', 'Auth\ResetPasswordController@reset');
Route::post('email/verify/{user}', 'Auth\VerificationController@verify')->name('verification.verify');
Route::post('email/resend', 'Auth\VerificationController@resend');
Route::post('oauth/{driver}', 'Auth\OAuthController@redirectToProvider')->name('oauth.redirect');
Route::get('oauth/{driver}/callback', 'Auth\OAuthController@handleProviderCallback')->name('oauth.callback');
});
Route::post('logout', 'Auth\LoginController@logout')->name('logout');
Tôi đã tìm ra vấn đề sau khi tôi gặp một lỗi kỳ lạ khác về việc RequestGuard::logout()
không tồn tại.
Nó khiến tôi nhận ra rằng các tuyến xác thực tùy chỉnh của tôi đang gọi các phương thức từ đặc điểm AuthenticatesUsers, nhưng tôi đã không sử dụng Auth::routes()
để thực hiện nó. Sau đó, tôi nhận ra rằng Laravel sử dụng trình bảo vệ web theo mặc định và điều đó có nghĩa là các tuyến đường phải ở trong routes/web.php
.
Đây là những gì cài đặt của tôi bây giờ trông như thế nào với Sanctum và một ứng dụng Vue SPA tách rời:
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' => [
EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
'throttle:60,1',
],
];
Lưu ý: Với Laravel Sanctum và Vue SPA cùng miền, bạn sử dụng httpOnly cookie cho cookie phiên và cookie của tôi, và cookie không an toàn cho CSRF, vì vậy bạn sử dụng web
bảo vệ cho auth và mọi tuyến trả về JSON được bảo vệ khác nên sử dụng auth:sanctum
phần mềm trung gian.
config / auth.php
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
...
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
Sau đó, bạn có thể có kiểm tra đơn vị như thế này, nơi mà giới phê bình, Auth::check()
, Auth::user()
, và Auth::logout()
làm việc như mong đợi với cấu hình tối thiểu và sử dụng tối đa của AuthenticatesUsers
và RegistersUsers
đặc điểm.
Dưới đây là một số thử nghiệm đơn vị đăng nhập của tôi:
TestCase.php
/**
* Creates and/or returns the designated regular user for unit testing
*
* @return \App\User
*/
public function user() : User
{
$user = User::query()->firstWhere('email', 'test-user@example.com');
if ($user) {
return $user;
}
// User::generate() is just a wrapper around User::create()
$user = User::generate('Test User', 'test-user@example.com', self::AUTH_PASSWORD);
return $user;
}
/**
* Resets AuthManager state by logging out the user from all auth guards.
* This is used between unit tests to wipe cached auth state.
*
* @param array $guards
* @return void
*/
protected function resetAuth(array $guards = null) : void
{
$guards = $guards ?: array_keys(config('auth.guards'));
foreach ($guards as $guard) {
$guard = $this->app['auth']->guard($guard);
if ($guard instanceof SessionGuard) {
$guard->logout();
}
}
$protectedProperty = new \ReflectionProperty($this->app['auth'], 'guards');
$protectedProperty->setAccessible(true);
$protectedProperty->setValue($this->app['auth'], []);
}
LoginTest.php
protected $auth_guard = 'web';
/** @test */
public function it_can_login()
{
$user = $this->user();
$this->postJson(route('login'), ['email' => $user->email, 'password' => TestCase::AUTH_PASSWORD])
->assertStatus(200)
->assertJsonStructure([
'user' => [
...expectedUserFields,
],
]);
$this->assertEquals(Auth::check(), true);
$this->assertEquals(Auth::user()->email, $user->email);
$this->assertAuthenticated($this->auth_guard);
$this->assertAuthenticatedAs($user, $this->auth_guard);
$this->resetAuth();
}
/** @test */
public function it_can_logout()
{
$this->actingAs($this->user())
->postJson(route('logout'))
->assertStatus(204);
$this->assertGuest($this->auth_guard);
$this->resetAuth();
}
Tôi đã ghi đè các registered
và authenticated
phương thức trong các đặc điểm xác thực Laravel để chúng trả về đối tượng người dùng thay vì chỉ 204 TÙY CHỌN:
public function authenticated(Request $request, User $user)
{
return response()->json([
'user' => $user,
]);
}
protected function registered(Request $request, User $user)
{
return response()->json([
'user' => $user,
]);
}
Xem mã nhà cung cấp để biết các đặc điểm xác thực. Bạn có thể sử dụng chúng một cách nguyên vẹn, cộng với hai phương pháp trên.
- nhà cung cấp / laravel / ui / auth-backend / RegistersUsers.php
- nhà cung cấp / laravel / ui / auth-backend / AuthenticatesUsers.php
Đây là hành động Vuex của Vue SPA của tôi để đăng nhập:
async login({ commit }, credentials) {
try {
const { data } = await axios.post(route('login'), {
...credentials,
remember: credentials.remember || undefined,
});
commit(FETCH_USER_SUCCESS, { user: data.user });
commit(LOGIN);
return commit(CLEAR_INTENDED_URL);
} catch (err) {
commit(LOGOUT);
throw new Error(`auth/login# Problem logging user in: ${err}.`);
}
},
async logout({ commit }) {
try {
await axios.post(route('logout'));
return commit(LOGOUT);
} catch (err) {
commit(LOGOUT);
throw new Error(`auth/logout# Problem logging user out: ${err}.`);
}
},
Tôi đã mất hơn một tuần để kiểm tra đơn vị xác thực của Laravel Sanctum + Vue SPA + cùng miền đều hoạt động theo tiêu chuẩn của tôi, vì vậy, hy vọng câu trả lời của tôi ở đây có thể giúp tiết kiệm thời gian cho người khác trong tương lai.