重置密码
这是一篇协同翻译的文章,你可以点击『我来翻译』按钮来参与翻译。
Resetting Passwords
Introduction
Most web applications provide a way for users to reset their forgotten passwords. Rather than forcing you to re-implement this by hand for every application you create, Laravel provides convenient services for sending password reset links and secure resetting passwords.
[!NOTE]
Want to get started fast? Install a Laravel application starter kit in a fresh Laravel application. Laravel's starter kits will take care of scaffolding your entire authentication system, including resetting forgotten passwords.
Model Preparation
Before using the password reset features of Laravel, your application's App\Models\User
model must use the Illuminate\Notifications\Notifiable
trait. Typically, this trait is already included on the default App\Models\User
model that is created with new Laravel applications.
Next, verify that your App\Models\User
model implements the Illuminate\Contracts\Auth\CanResetPassword
contract. The App\Models\User
model included with the framework already implements this interface, and uses the Illuminate\Auth\Passwords\CanResetPassword
trait to include the methods needed to implement the interface.
Database Preparation
A table must be created to store your application's password reset tokens. Typically, this is included in Laravel's default 0001_01_01_000000_create_users_table.php
database migration.
Configuring Trusted Hosts
默认情况下,无论 HTTP 请求的 Host
标头内容如何,Laravel 都会响应它收到的所有请求。此外,在 Web 请求期间生成应用程序的绝对 URL 时,将使用 Host
标头的值。
通常,你应该配置你的 Web 服务器(例如 Nginx 或 Apache),使其仅将与给定主机名匹配的请求发送到你的应用程序。但是,如果你无法直接自定义 Web 服务器,并需要指示 Laravel 仅响应某些主机名,则可以通过在应用程序的 bootstrap/app.php
文件中使用 trustHosts
中间件方法来实现。当你的应用程序提供密码重置功能时,这一点尤其重要。
要了解有关此中间件方法的更多信息,请查阅 TrustHosts 中间件文档。
路由
为了正确实现允许用户重置密码的支持,我们需要定义几个路由。首先,我们需要一对路由来处理允许用户通过其电子邮件地址请求密码重置链接。其次,我们需要一对路由来处理用户访问通过电子邮件发送给他们的密码重置链接并完成密码重置表单后实际重置密码的操作。
请求密码重置链接
密码重置链接请求表单
首先,我们将定义请求密码重置链接所需的路由。首先,我们将定义一个返回带有密码重置链接请求表单的视图的路由:
Route::get('/forgot-password', function () {
return view('auth.forgot-password');
})->middleware('guest')->name('password.request');
The view that is returned by this route should have a form containing an email
field, which will allow the user to request a password reset link for a given email address.
Handling the Form Submission
Next, we will define a route that handles the form submission request from the "forgot password" view. This route will be responsible for validating the email address and sending the password reset request to the corresponding user:
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');
Before moving on, let's examine this route in more detail. First, the request's email
attribute is validated. Next, we will use Laravel's built-in "password broker" (via the Password
facade) to send a password reset link to the user. The password broker will take care of retrieving the user by the given field (in this case, the email address) and sending the user a password reset link via Laravel's built-in notification system.
The sendResetLink
method returns a "status" slug. This status may be translated using Laravel's localization helpers in order to display a user-friendly message to the user regarding the status of their request. The translation of the password reset status is determined by your application's lang/{lang}/passwords.php
language file. An entry for each possible value of the status slug is located within the passwords
language file.
[!NOTE]
By default, the Laravel application skeleton does not include thelang
directory. If you would like to customize Laravel's language files, you may publish them via thelang:publish
Artisan command.
You may be wondering how Laravel knows how to retrieve the user record from your application's database when calling the Password
facade's sendResetLink
method. The Laravel password broker utilizes your authentication system's "user providers" to retrieve database records. The user provider used by the password broker is configured within the passwords
configuration array of your config/auth.php
configuration file. To learn more about writing custom user providers, consult the authentication documentation.
[!NOTE]
When manually implementing password resets, you are required to define the contents of the views and routes yourself. If you would like scaffolding that includes all necessary authentication and verification logic, check out the Laravel application starter kits.
Resetting the Password
The Password Reset Form
Next, we will define the routes necessary to actually reset the password once the user clicks on the password reset link that has been emailed to them and provides a new password. First, let's define the route that will display the reset password form that is displayed when the user clicks the reset password link. This route will receive a token
parameter that we will use later to verify the password reset request:
Route::get('/reset-password/{token}', function (string $token) {
return view('auth.reset-password', ['token' => $token]);
})->middleware('guest')->name('password.reset');
The view that is returned by this route should display a form containing an email
field, a password
field, a password_confirmation
field, and a hidden token
field, which should contain the value of the secret $token
received by our route.
处理表单提交
当然,我们需要定义一个路由来实际处理密码重置表单提交。此路由将负责验证传入请求并在数据库中更新用户密码:
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
外观)来验证密码重置请求凭据。
如果提供给密码代理的令牌、电子邮件地址和密码有效,则将调用传递给 reset
方法的闭包。在此闭包中,它接收用户提供给密码重置表单的用户实例和明文密码,我们可以更新数据库中的用户密码。
reset
方法返回一个「状态」标识符。此状态可以使用 Laravel 的 本地化 助手进行翻译,以便向用户显示关于其请求状态的用户友好消息。密码重置状态的翻译由应用程序的 lang/{lang}/passwords.php
语言文件确定。状态标识符的每个可能值在 passwords
语言文件中都有一个条目。如果您的应用程序不包含 lang
目录,您可以使用 lang:publish
Artisan 命令创建它。
在继续之前,您可能想知道在调用 Password
外观的 reset
方法时,Laravel 是如何从应用程序数据库中检索用户记录的。Laravel 密码代理利用身份验证系统的「用户提供者」来检索数据库记录。密码代理使用的用户提供者在 config/auth.php
配置文件的 passwords
配置数组中配置。要了解更多关于编写自定义用户提供者的信息,请参阅 身份验证文档。
删除过期令牌
已过期的密码重置令牌仍将存在于您的数据库中。不过,您可以使用 auth:clear-resets
Artisan 命令轻松删除这些记录:
php artisan auth:clear-resets
如果您想自动化此过程,请考虑将该命令添加到应用程序的 调度器 中:
use Illuminate\Support\Facades\Schedule;
Schedule::command('auth:clear-resets')->everyFifteenMinutes();
自定义
重置链接自定义
您可以使用 ResetPassword
通知类提供的 createUrlUsing
方法自定义密码重置链接 URL。此方法接受一个闭包,该闭包接收正在接收通知的用户实例以及密码重置链接令牌。通常,您应该从 App\Providers\AppServiceProvider
服务提供者的 boot
方法中调用此方法:
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 并将你的通知发送给用户:
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));
}
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: