跳至内容

重置密码

介绍

大多数 Web 应用程序都提供了重置用户忘记密码的功能。Laravel 提供了便捷的服务,用于发送密码重置链接和安全的密码重置,无需您为每个应用程序手动重新实现此功能。

想要快速上手?在全新的 Laravel 应用程序中安装 Laravel应用程序入门套件。Laravel 的入门套件将负责搭建您的整个身份验证系统,包括重置忘记的密码。

模型准备

在使用 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 的默认0001_01_01_000000_create_users_table.php数据库迁移中。

配置可信主机

默认情况下,Laravel 会响应所有收到的请求,无论 HTTP 请求标头的内容如何Host。此外,Host在 Web 请求期间,标头的值将用于生成应用程序的绝对 URL。

通常,你应该配置你的 Web 服务器(例如 Nginx 或 Apache),使其仅向你的应用程序发送与给定主机名匹配的请求。但是,如果你无法直接自定义 Web 服务器,并且需要指示 Laravel 仅响应某些主机名,则可以通过trustHosts在应用程序bootstrap/app.php文件中使用中间件方法来执行此操作。当你的应用程序提供密码重置功能时,这一点尤为重要。

要了解有关此中间件方法的更多信息,请查阅TrustHosts 中间件文档

路由

为了正确实现允许用户重置密码的功能,我们需要定义几个路由。首先,我们需要一对路由来处理允许用户通过其电子邮件地址请求密码重置链接的操作。其次,我们需要一对路由来处理用户访问通过电子邮件发送的密码重置链接并填写密码重置表单后实际执行的密码重置操作。

首先,我们将定义请求密码重置链接所需的路由。首先,我们将定义一个路由,该路由返回一个包含密码重置链接请求表单的视图:

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

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

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

1use Illuminate\Http\Request;
2use Illuminate\Support\Facades\Password;
3 
4Route::post('/forgot-password', function (Request $request) {
5 $request->validate(['email' => 'required|email']);
6 
7 $status = Password::sendResetLink(
8 $request->only('email')
9 );
10 
11 return $status === Password::ResetLinkSent
12 ? back()->with(['status' => __($status)])
13 : back()->withErrors(['email' => __($status)]);
14})->middleware('guest')->name('password.email');

在继续之前,让我们更详细地研究一下这个路由。首先,email验证请求的属性。接下来,我们将使用 Laravel 内置的“密码代理”(通过外观)向用户发送密码重置链接。密码代理将负责通过给定的字段(在本例中为电子邮件地址)检索用户,并通过 Laravel 内置的通知系统Password向用户发送密码重置链接

sendResetLink方法返回一个“状态”slug。可以使用 Laravel 的本地化Helpers翻译此状态,以便向用户显示有关其请求状态的友好消息。密码重置状态的翻译由应用程序的lang/{lang}/passwords.php语言文件决定。状态 slug 的每个可能值都位于passwords语言文件中。

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

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

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

重置密码

密码重置表格

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

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

此路由返回的视图应显示一个包含email字段、password字段、password_confirmation字段和隐藏字段的表单,其中应包含我们的路由收到的token秘密的值。$token

处理表单提交

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

1use App\Models\User;
2use Illuminate\Auth\Events\PasswordReset;
3use Illuminate\Http\Request;
4use Illuminate\Support\Facades\Hash;
5use Illuminate\Support\Facades\Password;
6use Illuminate\Support\Str;
7 
8Route::post('/reset-password', function (Request $request) {
9 $request->validate([
10 'token' => 'required',
11 'email' => 'required|email',
12 'password' => 'required|min:8|confirmed',
13 ]);
14 
15 $status = Password::reset(
16 $request->only('email', 'password', 'password_confirmation', 'token'),
17 function (User $user, string $password) {
18 $user->forceFill([
19 'password' => Hash::make($password)
20 ])->setRememberToken(Str::random(60));
21 
22 $user->save();
23 
24 event(new PasswordReset($user));
25 }
26 );
27 
28 return $status === Password::PasswordReset
29 ? redirect()->route('login')->with('status', __($status))
30 : back()->withErrors(['email' => [__($status)]]);
31})->middleware('guest')->name('password.update');

在继续之前,让我们更详细地研究一下这个路由。首先,验证请求的tokenemailpassword属性。接下来,我们将使用 Laravel 内置的“密码代理”(通过PasswordFacade)来验证密码重置请求的凭据。

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

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

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

删除过期的令牌

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

1php artisan auth:clear-resets

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

1use Illuminate\Support\Facades\Schedule;
2 
3Schedule::command('auth:clear-resets')->everyFifteenMinutes();

定制

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

1use App\Models\User;
2use Illuminate\Auth\Notifications\ResetPassword;
3 
4/**
5 * Bootstrap any application services.
6 */
7public function boot(): void
8{
9 ResetPassword::createUrlUsing(function (User $user, string $token) {
10 return 'https://example.com/reset-password?token='.$token;
11 });
12}

重置电子邮件定制

您可以轻松修改用于向用户发送密码重置链接的通知类。首先,请重写模型sendPasswordResetNotification上的方法。在此方法中,您可以使用自己创建的任何通知类App\Models\User发送通知。密码重置链接是该方法接收的第一个参数。您可以使用它来构建您选择的密码重置 URL,并将通知发送给用户:$token$token

1use App\Notifications\ResetPasswordNotification;
2 
3/**
4 * Send a password reset notification to the user.
5 *
6 * @param string $token
7 */
8public function sendPasswordResetNotification($token): void
9{
10 $url = 'https://example.com/reset-password?token='.$token;
11 
12 $this->notify(new ResetPasswordNotification($url));
13}