重置密码
介绍
大多数 Web 应用程序都提供了一种让用户重置忘记密码的方法。Laravel 提供了方便的服务来发送密码重置链接和安全地重置密码,而不是让你为每个应用程序手动重新实现这一功能。
想快速开始吗?在一个新的 Laravel 应用程序中安装一个 应用程序启动套件。Laravel 的启动套件将为你处理整个身份验证系统的脚手架,包括重置忘记的密码。
模型准备
在使用 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 应用程序中,所以你只需要迁移你的数据库来创建这个表:
php artisan migrate
配置可信主机
默认情况下,Laravel 将响应它接收到的所有请求,而不管 HTTP 请求的 Host
头的内容。此外,Host
头的值将在 Web 请求期间生成应用程序的绝对 URL 时使用。
通常,你应该配置你的 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');
在继续之前,让我们更详细地检查此路由。首先,验证请求的 email
属性。接下来,我们将使用 Laravel 的内置“密码代理”(通过 Password
facade)向用户发送密码重置链接。密码代理将负责通过给定字段(在本例中为电子邮件地址)检索用户并通过 Laravel 的内置 通知系统 向用户发送密码重置链接。
sendResetLink
方法返回一个“状态”标识符。可以使用 Laravel 的 本地化 助手来翻译此状态,以便向用户显示有关其请求状态的用户友好消息。密码重置状态的翻译由应用程序的 lang/{lang}/passwords.php
语言文件确定。每个可能的状态标识符的条目都位于 passwords
语言文件中。
你可能想知道 Laravel 如何在调用 Password
facade 的 sendResetLink
方法时从应用程序的数据库中检索用户记录。Laravel 密码代理利用身份验证系统的“用户提供者”来检索数据库记录。密码代理使用的用户提供者在 config/auth.php
配置文件的 passwords
配置数组中配置。要了解有关编写自定义用户提供者的更多信息,请查阅 身份验证文档。
在手动实现密码重置时,你需要自己定义视图和路由的内容。如果你想要包含所有必要的身份验证和验证逻辑的脚手架,请查看 Laravel 应用程序启动套件。
重置密码
密码重置表单
接下来,我们将定义用户点击通过电子邮件发送的密码重置链接并提供新密码后实际重置密码所需的路由。首先,让我们定义一个路由,该路由将显示用户点击重置密码链接时显示的重置密码表单。此路由将接收一个 token
参数,我们将在稍后用于验证密码重置请求:
Route::get('/reset-password/{token}', function ($token) {
return view('auth.reset-password', ['token' => $token]);
})->middleware('guest')->name('password.reset');
此路由返回的视图应显示一个包含 email
字段、password
字段、password_confirmation
字段和一个隐藏的 token
字段的表单,该字段应包含我们路由接收到的秘密 $token
的值。
处理表单提交
当然,我们需要定义一个路由来实际处理密码重置表单的提交。此路由将负责验证传入的请求并更新数据库中的用户密码:
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, $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');
在继续之前,让我们更详细地检查此路由。首先,验证请求的 token
、email
和 password
属性。接下来,我们将使用 Laravel 的内置“密码代理”(通过 Password
facade)来验证密码重置请求凭据。
如果提供给密码代理的令牌、电子邮件地址和密码有效,则将调用传递给 reset
方法的闭包。在此闭包中,它接收用户实例和提供给密码重置表单的明文密码,我们可以在数据库中更新用户的密码。
reset
方法返回一个“状态”标识符。可以使用 Laravel 的 本地化 助手来翻译此状态,以便向用户显示有关其请求状态的用户友好消息。密码重置状态的翻译由应用程序的 lang/{lang}/passwords.php
语言文件确定。每个可能的状态标识符的条目都位于 passwords
语言文件中。
在继续之前,你可能想知道 Laravel 如何在调用 Password
facade 的 reset
方法时从应用程序的数据库中检索用户记录。Laravel 密码代理利用身份验证系统的“用户提供者”来检索数据库记录。密码代理使用的用户提供者在 config/auth.php
配置文件的 passwords
配置数组中配置。要了解有关编写自定义用户提供者的更多信息,请查阅 身份验证文档。
删除过期令牌
过期的密码重置令牌仍将存在于你的数据库中。但是,你可以使用 auth:clear-resets
Artisan 命令轻松删除这些记录:
php artisan auth:clear-resets
如果你想自动化此过程,请考虑将命令添加到应用程序的 调度器 中:
$schedule->command('auth:clear-resets')->everyFifteenMinutes();
自定义
重置链接自定义
你可以使用 ResetPassword
通知类提供的 createUrlUsing
方法自定义密码重置链接 URL。此方法接受一个闭包,该闭包接收接收通知的用户实例以及密码重置链接令牌。通常,你应该从 App\Providers\AuthServiceProvider
服务提供者的 boot
方法中调用此方法:
use Illuminate\Auth\Notifications\ResetPassword;
/**
* 注册任何身份验证/授权服务。
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
ResetPassword::createUrlUsing(function ($user, string $token) {
return 'https://example.com/reset-password?token='.$token;
});
}
重置邮件自定义
你可以轻松修改用于向用户发送密码重置链接的通知类。首先,覆盖 App\Models\User
模型上的 sendPasswordResetNotification
方法。在此方法中,你可以使用任何你自己创建的 通知类 发送通知。密码重置 $token
是方法接收到的第一个参数。你可以使用此 $token
构建你选择的密码重置 URL 并向用户发送通知:
use App\Notifications\ResetPasswordNotification;
/**
* 向用户发送密码重置通知。
*
* @param string $token
* @return void
*/
public function sendPasswordResetNotification($token)
{
$url = 'https://example.com/reset-password?token='.$token;
$this->notify(new ResetPasswordNotification($url));
}