介绍

大多数Web运用都为用户提供重置和忘记密码的方法。Laravel已经便捷的服务来提供发送密码重置链接和安全重置密码的,而不需要您为每个运用程序重新实现此功能。


[!注意]
想快速上手吗? 在全新的Laravel运用程序中安装Laravel运用程序入门套件。 Laravel的入门套件将负责为您的整个身份验证系统包括重置和忘记密码提供支持。

模型准备

在使用Laravel的密码重置功能之前,运用程序的 php App\Models\User 模型必须使用 php Illuminate\Notifications\Notifiable trait。通常,在新创建的Laravel运用程序的 php App\Models\User 模型中默认引入了该trait。

接下来, 验证 php App\Models\User 是否实现了 php Illuminate\Contracts\Auth\CanResetPassword 协定。 框架附带的 php App\Models\User 模型已实现此接口,并使用 php Illuminate\Auth\Passwords\CanResetPassword 特征来包含实现接口所需的方法。

数据库准备

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

配置可信主机

默认情况下, 无论HTTP请求的php Host头的内容是什么,Laravel都会响应它收到的所有请求。此外,在Web请求期间生成运用程序的绝对URL时,将使用 php Host 标头的值。

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

要了解有关此中间件的更多信息,请参阅 php TrustHosts 中间件文档.

路由

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

请求重置密码链接

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

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

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

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

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

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

php sendResetLink 方法将返回一个状态标识。可以使用Laravel的 本地化 助手来转化此状态,以便向用户显示有关请求状态的用户友好消息。密码重置状态的转换由运用程序的 php lang/{lang}/passwords.php 语言文件来决定。 状态字段的每个可能值的条目位于php passwords语言文件中。


[!注意]
默认情况下,Laravel运用程序的矿建不包含 php lang 目录。 如果你想定制Laravel的语言文件,您可以通过 Artisan命令的php lang:publish来发布。


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


[!注意]
手动实现密码重置时,需要自行定义视图和路由的内容。如果您想要包含所有必要的身份验证和验证逻辑的基架,请参阅Laravel入门工具包.

重置密码

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

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

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

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

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

继续之前,让我们更详细的研究一下这个路由。首先,验证请求的 php tokenphp email, 和 php password 字段。 接下来, 我们将使用Laravel的内置 「密码代理」 (通过php PasswordFacade) 来验证密码重置请求凭据。

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

该方法返回一个「状态」标识。可以使用Laravel的 本地化 php reset助手翻译此状态,以便向用户显示有关其请求状态的用户友好消息。密码重置状态的翻译由运用程序的语言文件php lang/{lang}/passwords.php决定,状态标识每个可能值的条目位于php passwords语言文件决定。如果您的运用冲虚没有 php lang 文件夹, 您可以使用Artisan的php lang:publish命令来创建。

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

删除过期令牌

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

  1. php artisan auth:clear-resets

如果您想自动化这个过程,请考虑命令增加到您的运用程序的 任务调度中:

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

定制

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

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

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

  1. use App\Notifications\ResetPasswordNotification;
  2. /**
  3. * 发送密码重置通知给用户.
  4. *
  5. * @param string $token
  6. */
  7. public function sendPasswordResetNotification($token): void
  8. {
  9. $url = 'https://example.com/reset-password?token='.$token;
  10. $this->notify(new ResetPasswordNotification($url));
  11. }