Любите загадки? Событие еще доступно на сайте.
Поддержите нас - каждый вклад важен!
Поддержите нас - каждый вклад важен!

Сброс пароля

Вы просматриваете документ для прошлой версии.
Рассмотрите возможность обновления вашего проекта до актуальной версии 11.x. Почему это важно?

Введение

Большинство веб-приложений предоставляют пользователям возможность сбросить забытые пароли. Вместо того чтобы заставлять вас заново реализовывать этот функционал самостоятельно для каждого создаваемого вами приложения, Laravel предлагает удобные сервисы для отправки ссылок для сброса пароля и, собственно, безопасного сброса паролей.

Хотите быстро начать? Установите один из стартовых комплектов в новое приложение Laravel. Стартовые комплекты позаботятся о построении всей вашей системы аутентификации, включая сброс забытых паролей.

Подготовка модели

Перед использованием функционала сброса пароля Laravel модель вашего приложения App\Models\User должна использовать трейт Illuminate\Notifications\Notifiable. Обычно этот трейт уже содержится по умолчанию в модели App\Models\User при создании новых приложений Laravel.

Затем убедитесь, что ваша модель App\Models\User реализует контракт Illuminate\Contracts\Auth\CanResetPassword. Модель App\Models\User Laravel, уже реализует этот интерфейс и использует трейт Illuminate\Auth\Passwords\CanResetPassword, включающий методы, необходимые для реализации интерфейса.

Подготовка базы данных

Необходимо создать таблицу для сохранения токенов сброса пароля вашего приложения. Миграция для этой таблицы содержится по умолчанию в Laravel, поэтому вам нужно только выполнить миграцию БД для создания этой таблицы:

php artisan migrate

Конфигурирование доверенных хостов

По умолчанию Laravel будет отвечать на все запросы, которые он получает, независимо от содержимого заголовка Host HTTP-запроса. Кроме того, значение заголовка Host будет использоваться при генерации абсолютных URL-адресов вашего приложения во время веб-запроса.

Как правило, вам следует настроить свой веб-сервер (Nginx или Apache), так, чтобы он обслуживал запросы, соответствующие только указанному имени хоста. Однако, если у вас нет возможности напрямую настроить свой веб-сервер и вам нужно указать Laravel, чтобы он отвечал только на определенные имена хостов, вы можете сделать это, задействовав посредник App\Http\Middleware\TrustHosts для вашего приложения. Это особенно важно, когда ваше приложение предлагает функционал сброса пароля.

Чтобы узнать больше об этом посреднике, обратитесь к документации посредника TrustHosts.

Маршрутизация

Чтобы правильно реализовать поддержку, позволяющую пользователям сбрасывать свои пароли, нам нужно будет определить несколько маршрутов. Во-первых, нам понадобится пара маршрутов для обработки, позволяющей пользователю запрашивать ссылку для сброса пароля через свой адрес электронной почты. Во-вторых, нам понадобится пара маршрутов для обработки фактического сброса пароля при посещении пользователем ссылки для сброса пароля, отправленной ему по электронной почте, и последующего заполнения формы сброса пароля.

Сначала мы определим маршруты, которые необходимы для запроса ссылок для сброса пароля. Для начала мы определим маршрут, который возвращает шаблон с формой запроса ссылки для сброса пароля:

Route::get('/forgot-password', function () {
    return view('auth.forgot-password');
})->middleware('guest')->name('password.request');

Шаблон, возвращаемый этим маршрутом, должен иметь форму с полем email для указания адреса электронной почты, позволяющем пользователю запросить ссылку для сброса пароля.

Затем мы определим маршрут, который обрабатывает запрос на отправку формы из шаблона forgot-password. Этот маршрут будет отвечать за проверку адреса электронной почты и отправку запроса на сброс пароля соответствующему пользователю:

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::RESET_LINK_SENT
                ? back()->with(['status' => __($status)])
                : back()->withErrors(['email' => __($status)]);
})->middleware('guest')->name('password.email');

Прежде чем двигаться дальше, давайте рассмотрим этот маршрут более подробно. Сначала проверяется атрибут запроса email. Затем мы будем использовать встроенный в Laravel «брокер паролей» через фасад Password, чтобы отправить пользователю ссылку для сброса пароля. Брокер паролей позаботится о получении пользователя по указанному полю (в данном случае по адресу электронной почты) и отправит пользователю ссылку для сброса пароля через встроенную систему уведомлений Laravel.

Метод sendResetLink возвращает ключ «status». Этот статус может быть переведен с помощью помощников локализации Laravel, чтобы показать пользователю удобное сообщение о статусе его запроса. Перевод статуса сброса пароля определяется языковым файлом lang/{lang}/passwords.php вашего приложения. Запись для каждого возможного значения ключа статуса находится в языковом файле passwords.

По умолчанию скелет приложения Laravel не включает каталог lang. Если вы хотите настроить языковые файлы Laravel, вы можете опубликовать их с помощью команды lang:publish Artisan.

Вам может быть интересно: как 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 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::PASSWORD_RESET
                ? redirect()->route('login')->with('status', __($status))
                : back()->withErrors(['email' => [__($status)]]);
})->middleware('guest')->name('password.update');

Прежде чем двигаться дальше, давайте рассмотрим этот маршрут более подробно. Сначала проверяются атрибуты запроса token, email, и password. Далее мы будем использовать встроенный в Laravel «брокер паролей» (через фасад Password) для проверки учетных данных запроса сброса пароля.

Если токен, адрес электронной почты и пароль, переданные брокеру паролей, действительны, будет вызвано замыкание, переданное методу reset. В рамках этого замыкания, которое получает экземпляр пользователя и пароль в виде обычного текста из формы сброса пароля, мы можем обновить пароль пользователя в базе данных.

Метод reset возвращает ключ «status». Этот статус может быть переведен с помощью помощников локализации Laravel, чтобы показать пользователю удобное сообщение о статусе его запроса. Перевод статуса сброса пароля определяется языковым файлом lang/{lang}/passwords.php вашего приложения. Запись для каждого возможного значения ключа статуса находится в языковом файле passwords. Если ваше приложение не содержит каталога lang, вы можете создать его, используя команду Artisan lang:publish.

Прежде чем двигаться дальше, вам может быть интересно, как Laravel знает, как получить запись пользователя из базы данных вашего приложения при вызове метода reset фасада Password. Брокер паролей Laravel использует «поставщиков пользователей» вашей системы аутентификации для получения записей из базы данных. Поставщик пользователей, используемый брокером паролей, настраивается в массиве passwords вашего файла конфигурации config/auth.php. Чтобы узнать больше о создании пользовательских поставщиков служб, обратитесь к документации по аутентификации.

Удаление просроченных токенов

Токены сброса пароля с истекшим сроком действия будут по-прежнему присутствовать в вашей базе данных. Однако вы можете легко удалить эти записи, используя Artisan-команду auth:clear-resets:

php artisan auth:clear-resets

Если вы хотите автоматизировать этот процесс, рассмотрите возможность добавления команды в планировщик вашего приложения:

$schedule->command('auth:clear-resets')->everyFifteenMinutes();

Настройка

Вы можете изменить URL-адрес ссылки для сброса пароля, используя метод createUrlUsing класса уведомлений ResetPassword. Этот метод принимает замыкание, которое получает экземпляр ожидающего уведомление пользователя, а также токен ссылки для сброса пароля. Как правило, вызов этого метода осуществляется в методе boot вашего поставщика служб App\Providers\AuthServiceProvider:

use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;

/**
 * Регистрация любых служб аутентификации / авторизации.
 */
public function boot(): void
{
    $this->registerPolicies();

    ResetPassword::createUrlUsing(function (User $user, string $token) {
        return 'https://example.com/reset-password?token='.$token;
    });
}

Настройка уведомлений о сбросе пароля

Вы можете легко изменить класс уведомления, используемый для отправки пользователю ссылки для сброса пароля. Для начала переопределите метод sendPasswordResetNotification в модели App\Models\User. В этом методе вы можете отправить уведомление, используя любой класс уведомлений, созданный вами. Токен для сброса пароля – это первый аргумент, получаемый методом. Вы можете использовать этот $token для создания URL сброса пароля по вашему усмотрению и для дальнейшей отправки уведомления пользователю:

use App\Notifications\ResetPasswordNotification;

/**
 * Отправить пользователю уведомление о сбросе пароля.
 *
 * @param  string  $token
 */
public function sendPasswordResetNotification($token): void
{
    $url = 'https://example.com/reset-password?token='.$token;

    $this->notify(new ResetPasswordNotification($url));
}