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

重置密码 ​

介绍 ​

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

注意

想快速开始吗?在一个新的 Laravel 应用程序中安装一个 Laravel 应用程序启动套件。Laravel 的启动套件将负责搭建整个身份验证系统,包括重置忘记的密码。

配置 ​

你应用的密码重置配置文件位于 config/auth.php。请务必查看该文件中可用的选项。默认情况下,Laravel 配置为使用 database 密码重置驱动。

密码重置的 driver 配置选项定义了密码重置数据的存储位置。Laravel 提供了两种驱动:

  • database - 密码重置数据存储在关系型数据库中。
  • cache - 密码重置数据存储在你的某个基于缓存的存储中。

驱动程序先决条件 ​

数据库 ​

当使用默认的 database 驱动时,必须创建一张表来存储应用的密码重置令牌。通常,这包含在 Laravel 默认的 0001_01_01_000000_create_users_table.php 数据库迁移文件中。

缓存 ​

也可以使用缓存驱动来处理密码重置,这不需要专门的数据库表。每条记录以用户的邮箱地址作为键,因此请确保你没有在应用的其他地方将邮箱地址用作缓存键:

php
'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 trait。通常,这个 trait 已经包含在新创建的 Laravel 应用程序的默认 App\Models\User 模型中。

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

配置可信主机 ​

默认情况下,Laravel 将响应它接收到的所有请求,而不管 HTTP 请求的 Host 头的内容。此外,Host 头的值将在 Web 请求期间生成应用程序的绝对 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 配置数组中配置。要了解有关编写自定义用户提供者的更多信息,请查阅身份验证文档。

删除过期令牌 ​

如果你使用的是 database 驱动,已过期的密码重置令牌仍然会保留在你的数据库中。不过,你可以通过运行 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。此方法接受一个闭包,该闭包接收正在接收通知的用户实例以及密码重置链接令牌。通常,您应该从应用程序的 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));
}