Skip to content
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待

重置密码 ​

介绍 ​

大多数网络应用程序提供了一种方法,让用户重置他们忘记的密码。Laravel 提供了方便的服务,用于发送密码重置链接和安全地重置密码,而不必为每个创建的应用程序手动重新实现这一功能。

注意

想要快速入门?在一个新的 Laravel 应用程序中安装 Laravel 应用程序启动包。Laravel 的启动包将处理整个身份验证系统的脚手架,包括重置忘记的密码。

模型准备 ​

在使用 Laravel 的密码重置功能之前,您的应用程序的 App\Models\User 模型必须使用 Illuminate\Notifications\Notifiable 特性。通常,这个特性已经包含在新 Laravel 应用程序创建的默认 App\Models\User 模型中。

接下来,验证您的 App\Models\User 模型是否实现了 Illuminate\Contracts\Auth\CanResetPassword 合同。框架中包含的 App\Models\User 模型已经实现了这个接口,并使用 Illuminate\Auth\Passwords\CanResetPassword 特性来包含实现该接口所需的方法。

数据库准备 ​

必须创建一个表来存储您应用程序的密码重置令牌。通常,这在 Laravel 的默认 0001_01_01_000000_create_users_table.php 数据库迁移中包含。

配置受信任的主机 ​

默认情况下,Laravel 将响应它接收到的所有请求,而不考虑 HTTP 请求的 Host 头的内容。此外,Host 头的值将在生成绝对 URL 时用于您的应用程序。

通常,您应该配置您的 Web 服务器(如 Nginx 或 Apache),只发送与给定主机名匹配的请求到您的应用程序。然而,如果您无法直接自定义 Web 服务器,并且需要指示 Laravel 仅响应某些主机名,您可以通过在应用程序的 bootstrap/app.php 文件中使用 trustHosts 中间件方法来实现。这在您的应用程序提供密码重置功能时尤其重要。

要了解更多关于这个中间件方法的信息,请查阅 TrustHosts 中间件文档。

路由 ​

为了正确实现允许用户重置密码的支持,我们需要定义几个路由。首先,我们需要一对路由来处理允许用户通过他们的电子邮件地址请求密码重置链接。其次,我们需要一对路由来处理用户访问通过电子邮件发送给他们的密码重置链接并完成密码重置表单后,实际重置密码的过程。

请求密码重置链接 ​

密码重置链接请求表单 ​

首先,我们将定义请求密码重置链接所需的路由。要开始,我们将定义一个返回带有密码重置链接请求表单的视图的路由:

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

该路由返回的视图应包含一个 email 字段的表单,允许用户请求给定电子邮件地址的密码重置链接。

处理表单提交 ​

接下来,我们将定义一个处理“忘记密码”视图的表单提交请求的路由。该路由将负责验证电子邮件地址并向相应用户发送密码重置请求:

php
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 facade)向用户发送密码重置链接。密码代理将负责通过给定字段(在这种情况下为电子邮件地址)检索用户,并通过 Laravel 内置的 通知系统 发送密码重置链接。

sendResetLink 方法返回一个“状态”标识符。该状态可以使用 Laravel 的 本地化 帮助程序进行翻译,以便向用户显示有关其请求状态的用户友好消息。密码重置状态的翻译由您应用程序的 lang/{lang}/passwords.php 语言文件确定。每个可能的状态标识符的条目位于 passwords 语言文件中。

注意

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

您可能会想知道 Laravel 如何在调用 Password facade 的 sendResetLink 方法时知道如何从您应用程序的数据库中检索用户记录。Laravel 密码代理利用您身份验证系统的“用户提供者”来检索数据库记录。密码代理使用的用户提供者在您的 config/auth.php 配置文件的 passwords 配置数组中进行配置。要了解有关编写自定义用户提供者的更多信息,请查阅 身份验证文档。

注意

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

重置密码 ​

密码重置表单 ​

接下来,我们将定义必要的路由,以便在用户单击通过电子邮件发送给他们的密码重置链接并提供新密码时实际重置密码。首先,让我们定义一个路由,该路由将显示在用户单击重置密码链接时显示的重置密码表单。该路由将接收一个 token 参数,我们稍后将用它来验证密码重置请求:

php
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 的值。

处理表单提交 ​

当然,我们需要定义一个路由来实际处理密码重置表单的提交。该路由将负责验证传入请求并在数据库中更新用户的密码:

php
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 facade)来验证密码重置请求凭据。

如果提供给密码代理的令牌、电子邮件地址和密码有效,则将调用传递给 reset 方法的闭包。在这个闭包中,接收用户实例和提供给密码重置表单的明文密码,我们可以在数据库中更新用户的密码。

reset 方法返回一个“状态”标识符。该状态可以使用 Laravel 的 本地化 帮助程序进行翻译,以便向用户显示有关其请求状态的用户友好消息。密码重置状态的翻译由您应用程序的 lang/{lang}/passwords.php 语言文件确定。每个可能的状态标识符的条目位于 passwords 语言文件中。如果您的应用程序不包含 lang 目录,您可以使用 lang:publish Artisan 命令创建它。

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

删除过期令牌 ​

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

shell
php artisan auth:clear-resets

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

php
use Illuminate\Support\Facades\Schedule;

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

自定义 ​

重置链接自定义 ​

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

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

/**
 * 启动任何应用程序服务。
 */
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,并将通知发送给用户:

php
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));
}