密码重置


简介

大多数网站应用程序都提供了一种方式让用户重置他们遗忘的密码。为了不让你在每次创建新的应用程序时都需要手动去实现这个功能,Laravel 提供了方便的服务用于发送密码重置链接和安全地重置密码。

注意:想要快速开始?在新的 Laravel 应用程序中安装 Laravel 应用入门套件。Laravel 的启动工具包会为你构建整个身份认证系统,包括重置遗忘密码。

模型准备

在使用 Laravel 的密码重置功能之前,你的应用的 App\Models\User 模型必须使用 Illuminate\Notifications\Notifiable trait。一般来说,在新的 Laravel 应用程序中创建的默认 App\Models\User 模型已经包含了这个 trait。

接下来,确认你的 App\Models\User 模型实现了 Illuminate\Contracts\Auth\CanResetPassword 合同。框架已经在 App\Models\User 模型里实现了这个接口,并使用了 Illuminate\Auth\Passwords\CanResetPassword trait 以包括实现该接口所需的方法。

数据库准备

必须创建一个表来储存你的应用的密码重置令牌。在默认的 Laravel 应用程序中已经包含了这个表的迁移,所以你只需要迁移你的数据库来创建这个表:

php artisan migrate

配置可信主机

默认情况下,Laravel 会响应所有收到的请求,无论 HTTP 请求的 Host 标头的内容是什么。此外,生成 web 请求到你的应用的绝对 URL 时,Host 标头的值将会被使用。

通常,你应该配置你的 web 服务器,如 Nginx 或 Apache,仅发送与给定主机名匹配的请求到你的应用。但是如果你无法直接自定义你的 web 服务器,并需要告诉 Laravel 仅对某些主机名进行响应,你可以通过启用你的应用的 App\Http\Middleware\TrustHosts 中间件来实现。当你的应用提供密码重置功能时,这尤其重要。

想要了解更多有关这个中间件的信息,请参阅 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::RESET_LINK_SENT
                ? back()->with(['status' => __($status)])
                : back()->withErrors(['email' => __($status)]);

})->middleware('guest')->name('password.email');

在继续之前,让我们详细了解一下这个路由。首先,验证请求中的电子邮件属性。接下来,我们将使用 Laravel 内置的“密码券商”(通过 Password 门面)向用户发送重置密码链接。密码券商会处理通过给定字段(在这种情况下是电子邮件地址)检索用户,并通过 Laravel 内置的通知系统向用户发送重置密码链接。

sendResetLink 方法返回一个“status”标识。可以使用 Laravel 的本地化辅助函数来翻译此状态,以向用户显示有关其请求状态的友好提示。密码重置状态的翻译由应用程序的 lang/{lang}/passwords.php 语言文件确定。密码语言文件中包含了每个可能的状态标识值的条目。

默认情况下,Laravel 应用程序骨架不包含 lang 目录。如果您想自定义 Laravel 的语言文件,可以使用 lang:publish Artisan命令进行发布。

您可能想知道,当调用 Password 门面的 sendResetLink 方法时,Laravel 如何知道如何从您的应用程序数据库中检索用户记录。Laravel 密码券商使用身份认证系统的“用户提供者”来检索数据库记录。密码券商使用的用户提供器在您的 config/auth.php 配置文件的密码配置数组中进行配置。有关编写自定义用户提供器的更多信息,请参阅身份认证文档

在手动实现密码重置时,您需要自己定义视图和路由的内容。如果您想要包含所有必要的身份认证和验证逻辑的脚手架,请查看 Laravel 应用入门套件

重置密码

重置密码表单

接下来,我们将定义所需的路由来实际重置密码,一旦用户点击了通过电子邮件发送的重置密码链接并提供了新密码。首先,让我们定义一个路由,当用户点击重置密码链接时显示重置密码表单。这个路由将接收一个令牌参数,我们稍后将使用它来验证重置密码请求:

Route::get('/reset-password/{token}', function (string $token) {
    return view('auth.reset-password', ['token' => $token]);
})->middleware('guest')->name('password.reset');

这个路由返回的视图应该显示一个包含一个电子邮件字段、一个密码字段、一个密码确认字段和一个隐藏令牌字段的表单,该字段应该包含我们的路由接收到的秘密 $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::PASSWORD_RESET
                ? redirect()->route('login')->with('status', __($status))
                : back()->withErrors(['email' => [__($status)]]);

})->middleware('guest')->name('password.update');

在继续之前,让我们详细了解一下这个路由。首先,验证请求中的令牌、电子邮件和密码属性。接下来,我们将使用 Laravel 内置的“密码券商”(通过密码门面)来验证密码重置请求的凭据。

如果传给密码券商的令牌、电子邮件地址和密码有效,则会调用传递给reset方法的闭包。在这个闭包中,我们可以更新用户在数据库中的密码。

reset 方法返回一个“status”标识。可以使用 Laravel 的本地化辅助函数来翻译此状态,以向用户显示有关其请求状态的友好提示。密码重置状态的翻译由应用程序的 lang/{lang}/passwords.php 语言文件确定。密码语言文件中包含了每个可能的状态标识值的条目。如果您的应用程序中没有lang目录,可以使用 lang:publish Artisan命令创建它。

在继续之前,您可能想知道当调用 Password 门面的 reset 方法时,Laravel 如何知道如何从您的应用程序数据库中检索用户记录。Laravel 密码券商使用身份认证系统的“用户提供者”来检索数据库记录。密码券商使用的用户提供器在您的 config/auth.php 配置文件的密码配置数组中进行配置。有关编写自定义用户提供器的更多信息,请参阅身份认证文档

删除过期的令牌

已过期的密码重置令牌仍然会保留在数据库中。但是,您可以使用 auth:clear-resets Artisan命令轻松删除这些记录:

php artisan auth:clear-resets

如果您希望自动化此过程,请考虑将该命令添加到应用程序的调度程序中:

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

自定义

重置链接自定义

您可以使用 ResetPassword 通知类提供的 createUrlUsing 方法来自定义密码重置链接的URL。这个方法接受一个闭包,闭包接收接收通知的用户实例以及密码重置链接令牌。通常情况下,您应该在您的App\Providers\AuthServiceProvider 服务提供者的 boot 方法中调用这个方法:

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

/**
 * Register any authentication / authorization services.
 */
public function boot(): void
{
    ResetPassword::createUrlUsing(function (User $user, string $token) {
       return 'https://example.com/reset-password?token='.$token;
    });
}

重置电子邮件自定义

您可以轻松修改用于向用户发送密码重置链接的通知类。要开始,覆盖 App\Models\User 模型上的sendPasswordResetNotification 方法。在这个方法中,您可以使用您自己创建的任何通知类来发送通知。密码重置 $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));
}

点赞 取消点赞 收藏 取消收藏

<< 上一篇: 哈希

>> 下一篇: 入门指南