Скидання пароля · Laravel
Більшість вебзастосунків дає користувачам змогу відновити забутий пароль. Щоб не писати це вручну для кожного нового проєкту, Laravel надає готові сервіси для надсилання посилань на скидання пароля і безпечної зміни пароля.
Хочете почати швидко? Встановіть стартовий набір застосунку у свіжий Laravel-застосунок. Стартові набори Laravel створять усю систему автентифікації, включно зі скиданням забутих паролів.
Вступ
Конфігурація
Конфігураційний файл скидання пароля лежить у config/auth.php. Перегляньте доступні в ньому опції. За замовчуванням Laravel налаштований на драйвер скидання пароля database.
Опція driver визначає, де зберігатимуться дані скидання пароля. Laravel містить два драйвери:
database- дані скидання пароля зберігаються в реляційній базі даних.cache- дані скидання пароля зберігаються в одному зі сховищ кешу.
Вимоги драйверів
База даних
Для драйвера database, який використовується за замовчуванням, потрібна таблиця для зберігання токенів скидання пароля. Зазвичай вона вже є в стандартній міграції Laravel 0001_01_01_000000_create_users_table.php.
Кеш
Є також драйвер кешу для скидання паролів, який не потребує окремої таблиці в базі. Записи ключуються за email-адресою користувача, тож переконайтеся, що не використовуєте email-адреси як ключ кешу деінде у застосунку:
'passwords' => [
'users' => [
'driver' => 'cache',
'provider' => 'users',
'store' => 'passwords', // Необовʼязково...
'expire' => 60,
'throttle' => 60,
],
],
Щоб виклик artisan cache:clear не стирав дані скидання пароля, можна вказати окреме сховище кешу через ключ конфігурації store. Значення має відповідати сховищу, налаштованому в config/cache.php.
Підготовка моделі
Перш ніж користуватися можливостями скидання пароля в Laravel, модель App\Models\User має використовувати трейт Illuminate\Notifications\Notifiable. Зазвичай цей трейт уже підключений у стандартній моделі App\Models\User, яка створюється разом із новим Laravel-застосунком.
Далі переконайтеся, що модель App\Models\User реалізує контракт Illuminate\Contracts\Auth\CanResetPassword. Модель App\Models\User, що йде з фреймворком, уже реалізує цей інтерфейс і використовує трейт Illuminate\Auth\Passwords\CanResetPassword, який містить потрібні для цього методи.
Налаштування довірених хостів
За замовчуванням Laravel відповідає на всі запити, які отримує, незалежно від вмісту заголовка Host HTTP-запиту. Крім того, значення заголовка Host використовується при генерації абсолютних URL до вашого застосунку під час вебзапиту.
Зазвичай вебсервер (наприклад, Nginx або Apache) варто налаштувати так, щоб він передавав застосунку лише запити з певним іменем хоста. Але якщо можливості налаштувати вебсервер напряму немає і треба вказати Laravel відповідати тільки на певні імена хостів, скористайтеся методом middleware trustHosts у файлі bootstrap/app.php. Для функціональності скидання пароля це особливо важливо.
Докладніше про цей метод читайте в документації middleware TrustHosts.
Маршрутизація
Щоб коректно реалізувати скидання пароля користувачами, знадобиться визначити кілька маршрутів. Спершу пара маршрутів для запиту посилання на скидання пароля за email-адресою. Потім пара маршрутів для власне зміни пароля після того, як користувач перейде за надісланим посиланням і заповнить форму.
Запит посилання на скидання пароля
Форма запиту посилання на скидання пароля
Спершу визначимо маршрути, потрібні для запиту посилань на скидання пароля. Почнемо з маршруту, який повертає представлення (view) з формою запиту такого посилання:
Route::get('/forgot-password', function () {
return view('auth.forgot-password');
})->middleware('guest')->name('password.request');
Представлення, яке повертає цей маршрут, має містити форму з полем email: воно дасть користувачеві змогу запросити посилання на скидання пароля для конкретної email-адреси.
Обробка надсилання форми
Далі визначимо маршрут, який обробляє надсилання форми з представлення «забув пароль». Він відповідає за валідацію email-адреси і надсилання запиту на скидання пароля відповідному користувачеві:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
Route::post('/forgot-password', function (Request $request) {
$request->validate(['email' => 'required|email']);
$status = Password::sendResetLink(
$request->only('email')
);
return $status === Password::ResetLinkSent
? back()->with(['status' => __($status)])
: back()->withErrors(['email' => __($status)]);
})->middleware('guest')->name('password.email');
Розгляньмо цей маршрут детальніше. Спочатку валідується атрибут email із запиту. Потім вбудований у Laravel «password broker» (через фасад Password) надсилає користувачеві посилання на скидання пароля. Password broker сам знайде користувача за вказаним полем (у цьому випадку за email-адресою) і надішле йому посилання через вбудовану систему сповіщень Laravel.
Метод sendResetLink повертає слаг «статусу». Цей статус можна перекласти за допомогою помічників локалізації Laravel, щоб показати користувачеві зрозуміле повідомлення про стан його запиту. Переклад статусу скидання пароля визначається мовним файлом застосунку lang/{lang}/passwords.php. Запис для кожного можливого значення слага статусу знаходиться саме в мовному файлі passwords.
За замовчуванням каркас Laravel-застосунку не містить теки
lang. Якщо ви хочете налаштувати мовні файли Laravel, опублікуйте їх Artisan-командоюlang:publish.
Ви можете запитати, звідки Laravel знає, як дістати запис користувача з бази даних застосунку під час виклику методу sendResetLink фасаду Password. Password broker Laravel використовує «user providers» вашої системи автентифікації для отримання записів із бази. User provider, який використовує password broker, налаштовується в масиві конфігурації passwords файлу config/auth.php. Про написання власних user providers читайте в документації з автентифікації.
Коли ви реалізуєте скидання паролів вручну, вміст представлень і маршрути треба визначити самостійно. Якщо потрібен готовий каркас з усією логікою автентифікації та верифікації, погляньте на стартові набори застосунків Laravel.
Скидання пароля
Форма скидання пароля
Тепер визначимо маршрути, потрібні для власне скидання пароля після того, як користувач перейде за надісланим посиланням і введе новий пароль. Спершу маршрут, який показує форму скидання пароля при переході за посиланням. Цей маршрут отримує параметр token, який ми використаємо далі для перевірки запиту на скидання пароля:
Route::get('/reset-password/{token}', function (string $token) {
return view('auth.reset-password', ['token' => $token]);
})->middleware('guest')->name('password.reset');
Представлення, яке повертає цей маршрут, має показувати форму з полями email, password, password_confirmation і прихованим полем token, що містить значення секретного $token, отриманого нашим маршрутом.
Обробка надсилання форми
Звісно, потрібен маршрут, який обробить надсилання форми скидання пароля. Він відповідає за валідацію вхідного запиту й оновлення пароля користувача в базі даних:
use App\Models\User;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
Route::post('/reset-password', function (Request $request) {
$request->validate([
'token' => 'required',
'email' => 'required|email',
'password' => 'required|min:8|confirmed',
]);
$status = Password::reset(
$request->only('email', 'password', 'password_confirmation', 'token'),
function (User $user, string $password) {
$user->forceFill([
'password' => Hash::make($password)
])->setRememberToken(Str::random(60));
$user->save();
event(new PasswordReset($user));
}
);
return $status === Password::PasswordReset
? redirect()->route('login')->with('status', __($status))
: back()->withErrors(['email' => [__($status)]]);
})->middleware('guest')->name('password.update');
Розгляньмо цей маршрут детальніше. Спочатку валідуються атрибути запиту token, email і password. Потім вбудований у Laravel «password broker» (через фасад Password) перевіряє облікові дані запиту на скидання пароля.
Якщо переданий у password broker токен, email-адреса і пароль коректні, буде викликано замикання, передане в метод reset. Усередині цього замикання, яке отримує екземпляр користувача і пароль у відкритому вигляді з форми скидання, ми можемо оновити пароль користувача в базі даних.
Метод reset повертає слаг «статусу». Цей статус можна перекласти за допомогою помічників локалізації Laravel, щоб показати користувачеві зрозуміле повідомлення про стан його запиту. Переклад статусу скидання пароля визначається мовним файлом застосунку lang/{lang}/passwords.php. Запис для кожного можливого значення слага статусу знаходиться в мовному файлі passwords. Якщо у вашому застосунку немає теки lang, створіть її Artisan-командою lang:publish.
І знову ж таки, ви можете запитати, звідки Laravel знає, як дістати запис користувача з бази даних застосунку під час виклику методу reset фасаду Password. Password broker Laravel використовує «user providers» вашої системи автентифікації для отримання записів із бази. User provider, який використовує password broker, налаштовується в масиві конфігурації passwords файлу config/auth.php. Про написання власних user providers читайте в документації з автентифікації.
Видалення прострочених токенів
Якщо ви використовуєте драйвер database, прострочені токени скидання пароля залишаються в базі даних. Видалити ці записи легко Artisan-командою auth:clear-resets:
php artisan auth:clear-resets
Щоб автоматизувати цей процес, додайте команду до планувальника вашого застосунку:
use Illuminate\Support\Facades\Schedule;
Schedule::command('auth:clear-resets')->everyFifteenMinutes();
Налаштування
Налаштування посилання на скидання
URL посилання на скидання пароля можна налаштувати методом createUrlUsing, який надає клас сповіщення ResetPassword. Метод приймає замикання, що отримує екземпляр користувача, якому надсилається сповіщення, і токен посилання на скидання пароля. Зазвичай цей метод викликають у методі boot класу AppServiceProvider вашого застосунку:
use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
ResetPassword::createUrlUsing(function (User $user, string $token) {
return 'https://example.com/reset-password?token='.$token;
});
}
Налаштування листа про скидання
Клас сповіщення, яким користувачеві надсилається посилання на скидання пароля, легко змінити. Для цього перевизначте метод sendPasswordResetNotification у моделі App\Models\User. Усередині цього методу можна надіслати сповіщення будь-яким класом сповіщення власного авторства. Токен скидання пароля $token метод отримує першим аргументом. За цим $token ви можете побудувати потрібний URL скидання пароля і надіслати користувачеві своє сповіщення:
use App\Notifications\ResetPasswordNotification;
/**
* Send a password reset notification to the user.
*
* @param string $token
*/
public function sendPasswordResetNotification($token): void
{
$url = 'https://example.com/reset-password?token='.$token;
$this->notify(new ResetPasswordNotification($url));
}
Перекладаємо з офіційної документації, розділ за розділом, і не ховаємо недоперекладене. Помітили неточність у терміні чи реченні: напишіть, виправимо.