Скидання паролів
Вступ
Більшість вебзастосунків дають користувачам змогу скинути забутий пароль. Замість того щоб змушувати вас реалізовувати це вручну в кожному новому застосунку, Laravel надає зручні сервіси для надсилання посилань на скидання пароля та безпечного скидання паролів.
Хочете швидко почати? Встановіть стартовий набір застосунку Laravel у свіжий застосунок Laravel. Стартові набори Laravel створять усю вашу систему автентифікації, зокрема й скидання забутих паролів.
Конфігурація
Конфігураційний файл скидання паролів вашого застосунку розташований у config/auth.php. Обов'язково перегляньте доступні в ньому опції. За замовчуванням Laravel налаштований на драйвер скидання паролів database.
Опція конфігурації driver для скидання паролів визначає, де зберігатимуться дані скидання. Laravel містить два драйвери:
database- дані скидання пароля зберігаються в реляційній базі даних.cache- дані скидання пароля зберігаються в одному з ваших сховищ кешу.
Передумови для драйверів
База даних
Коли ви користуєтеся драйвером database за замовчуванням, потрібно створити таблицю для зберігання токенів скидання паролів вашого застосунку. Зазвичай вона вже є в стандартній міграції Laravel 0001_01_01_000000_create_users_table.php.
Кеш
Для обробки скидання паролів доступний також драйвер кешу, який не потребує окремої таблиці в базі даних. Записи мають ключем адресу електронної пошти користувача, тож переконайтеся, що ви не використовуєте адреси електронної пошти як ключі кешу деінде у вашому застосунку:
'passwords' => [
'users' => [
'driver' => 'cache',
'provider' => 'users',
'store' => 'passwords', // Optional...
'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.
Маршрутизація
Щоб належно реалізувати підтримку скидання паролів користувачами, нам знадобиться визначити кілька маршрутів. Спершу потрібна пара маршрутів, які дозволять користувачеві запросити посилання на скидання пароля за адресою електронної пошти. Далі потрібна пара маршрутів, які власне скидатимуть пароль, коли користувач перейде за надісланим йому посиланням і заповнить форму скидання.
Запит посилання для скидання пароля
Форма запиту посилання на скидання пароля
Спершу визначимо маршрути, потрібні для запиту посилань на скидання пароля. Для початку визначимо маршрут, який повертає представлення з формою запиту посилання:
Route::get('/forgot-password', function () {
return view('auth.forgot-password');
})->middleware('guest')->name('password.request');
Представлення, яке повертає цей маршрут, має містити форму з полем 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), щоб надіслати користувачеві посилання на скидання пароля. Брокер паролів подбає про пошук користувача за заданим полем (у цьому випадку - за адресою електронної пошти) і надішле йому посилання через вбудовану систему сповіщень Laravel.
Метод sendResetLink повертає слаг «статусу». Цей статус можна перекласти за допомогою хелперів локалізації Laravel, щоб показати користувачеві зрозуміле повідомлення про стан його запиту. Переклад статусу скидання пароля визначається мовним файлом lang/{lang}/passwords.php вашого застосунку. Запис для кожного можливого значення слага статусу міститься у мовному файлі passwords.
За замовчуванням каркас застосунку Laravel не містить каталогу
lang. Якщо ви хочете налаштувати мовні файли Laravel, опублікуйте їх артизан-командоюlang:publish.
Ви можете замислитися, звідки Laravel знає, як дістати запис користувача з бази даних вашого застосунку при виклику методу sendResetLink фасада Password. Брокер паролів Laravel використовує «провайдери користувачів» вашої системи автентифікації, щоб отримувати записи з бази даних. Провайдер користувачів, який використовує брокер паролів, налаштовується в масиві конфігурації passwords вашого файлу config/auth.php. Щоб дізнатися більше про написання власних провайдерів користувачів, зверніться до документації з автентифікації.
Реалізуючи скидання паролів вручну, ви маєте самі визначити вміст представлень і маршрутів. Якщо вам потрібен готовий каркас з усією логікою автентифікації та підтвердження, погляньте на стартові набори застосунку 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), щоб перевірити облікові дані запиту на скидання пароля.
Якщо переданий брокеру паролів токен, адреса електронної пошти та пароль дійсні, буде викликано замикання, передане методу reset. Усередині цього замикання, яке отримує екземпляр користувача та введений у форму пароль відкритим текстом, ми можемо оновити пароль користувача в базі даних.
Метод reset повертає слаг «статусу». Цей статус можна перекласти за допомогою хелперів локалізації Laravel, щоб показати користувачеві зрозуміле повідомлення про стан його запиту. Переклад статусу скидання пароля визначається мовним файлом lang/{lang}/passwords.php вашого застосунку. Запис для кожного можливого значення слага статусу міститься у мовному файлі passwords. Якщо у вашому застосунку немає каталогу lang, створіть його артизан-командою lang:publish.
Перш ніж рухатися далі, ви можете замислитися, звідки Laravel знає, як дістати запис користувача з бази даних вашого застосунку при виклику методу reset фасада Password. Брокер паролів Laravel використовує «провайдери користувачів» вашої системи автентифікації, щоб отримувати записи з бази даних. Провайдер користувачів, який використовує брокер паролів, налаштовується в масиві конфігурації passwords вашого файлу config/auth.php. Щоб дізнатися більше про написання власних провайдерів користувачів, зверніться до документації з автентифікації.
Видалення прострочених токенів
Якщо ви користуєтеся драйвером database, прострочені токени скидання паролів усе одно залишатимуться у вашій базі даних. Проте ви можете легко видалити ці записи артизан-командою 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));
}