跳轉至內容

重置密碼

簡介

大多數 Web 應用程式都為使用者提供了重置忘記密碼的方法。Laravel 無需你為每個建立的應用程式手動實現此功能,而是提供了便捷的服務,用於傳送密碼重置連結和安全地重置密碼。

想要快速上手?在一個全新的 Laravel 應用程式中安裝 Laravel 應用程式入門套件。Laravel 的入門套件將為你處理整個身份驗證系統的腳手架,包括重置忘記的密碼。

配置

應用程式的密碼重置配置檔案儲存在 config/auth.php 中。請務必檢視此檔案中可用的選項。預設情況下,Laravel 配置為使用 database 密碼重置驅動程式。

密碼重置的 driver 配置選項定義了密碼重置資料儲存的位置。Laravel 包含兩個驅動程式:

  • database - 密碼重置資料儲存在關係型資料庫中。
  • cache - 密碼重置資料儲存在你的快取驅動中。

驅動前提條件

資料庫

使用預設的 database 驅動時,必須建立一個表來儲存應用程式的密碼重置令牌。通常,這包含在 Laravel 預設的 0001_01_01_000000_create_users_table.php 資料庫遷移檔案中。

快取

還有一個用於處理密碼重置的快取驅動,它不需要專門的資料庫表。條目以使用者的電子郵件地址作為鍵名,因此請確保你在應用程式的其他地方沒有將電子郵件地址用作快取鍵。

1'passwords' => [
2 'users' => [
3 'driver' => 'cache',
4 'provider' => 'users',
5 'store' => 'passwords', // Optional...
6 'expire' => 60,
7 'throttle' => 60,
8 ],
9],

為了防止呼叫 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 來包含實現該介面所需的方法。

配置受信任的主機

預設情況下,無論 HTTP 請求的 Host 標頭內容如何,Laravel 都會響應它接收到的所有請求。此外,在 Web 請求期間生成指向應用程式的絕對 URL 時,將使用 Host 標頭的值。

通常,你應該配置 Web 伺服器(如 Nginx 或 Apache),使其僅將匹配給定主機名的請求傳送到你的應用程式。但是,如果你無法直接自定義 Web 伺服器,並且需要指示 Laravel 僅響應特定的主機名,可以透過在應用程式的 bootstrap/app.php 檔案中使用 trustHosts 中介軟體方法來實現。當你的應用程式提供密碼重置功能時,這一點尤為重要。

要了解有關此中介軟體方法的更多資訊,請參閱 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 內建的“密碼代理”(透過 Password 門面)向用戶傳送密碼重置連結。密碼代理將負責透過給定的欄位(在本例中為電子郵件地址)檢索使用者,並透過 Laravel 內建的 通知系統 向用戶傳送密碼重置連結。

sendResetLink 方法返回一個“狀態”標識。可以使用 Laravel 的 本地化 輔助函式來翻譯此狀態,以便向用戶顯示有關其請求狀態的友好訊息。密碼重置狀態的翻譯由應用程式的 lang/{lang}/passwords.php 語言檔案決定。狀態標識的每個可能值的條目都位於 passwords 語言檔案中。

預設情況下,Laravel 應用程式骨架不包含 lang 目錄。如果您想自定義 Laravel 的語言檔案,可以透過 lang:publish Artisan 命令釋出它們。

你可能想知道當呼叫 Password 門面的 sendResetLink 方法時,Laravel 是如何知道從應用程式資料庫中檢索使用者記錄的。Laravel 密碼代理利用你身份驗證系統的“使用者提供程式”來檢索資料庫記錄。密碼代理使用的使用者提供程式在 config/auth.php 配置檔案的 passwords 配置陣列中配置。要了解有關編寫自定義使用者提供程式的更多資訊,請查閱 身份驗證文件

手動實現密碼重置時,你需要自己定義檢視和路由的內容。如果你需要包含所有必要身份驗證和驗證邏輯的腳手架,請檢視 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 內建的“密碼代理”(透過 Password 門面)來驗證密碼重置請求憑據。

如果提供給密碼代理的令牌、電子郵件地址和密碼有效,則會呼叫傳遞給 reset 方法的閉包。在此閉包中(接收使用者例項和密碼重置表單提供的明文密碼),我們可以更新資料庫中的使用者密碼。

reset 方法返回一個“狀態”標識。可以使用 Laravel 的 本地化 輔助函式來翻譯此狀態,以便向用戶顯示有關其請求狀態的友好訊息。密碼重置狀態的翻譯由應用程式的 lang/{lang}/passwords.php 語言檔案決定。狀態標識的每個可能值的條目都位於 passwords 語言檔案中。如果你的應用程式不包含 lang 目錄,你可以使用 lang:publish Artisan 命令建立它。

在繼續之前,你可能想知道當呼叫 Password 門面的 reset 方法時,Laravel 是如何知道從應用程式資料庫中檢索使用者記錄的。Laravel 密碼代理利用你身份驗證系統的“使用者提供程式”來檢索資料庫記錄。密碼代理使用的使用者提供程式在 config/auth.php 配置檔案的 passwords 配置陣列中配置。要了解有關編寫自定義使用者提供程式的更多資訊,請查閱 身份驗證文件

刪除過期令牌

如果你使用的是 database 驅動,過期的密碼重置令牌仍會保留在資料庫中。但是,你可以使用 auth:clear-resets Artisan 命令輕鬆刪除這些記錄:

1php artisan auth:clear-resets

如果你想自動化此過程,請考慮將該命令新增到應用程式的 排程程式 中。

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

自定義

你可以使用 ResetPassword 通知類提供的 createUrlUsing 方法自定義密碼重置連結 URL。此方法接受一個閉包,該閉包接收正在接收通知的使用者例項以及密碼重置連結令牌。通常,你應該在應用程式 AppServiceProviderboot 方法中呼叫此方法:

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}

重置郵件自定義

你可以輕鬆修改用於向用戶傳送密碼重置連結的通知類。首先,覆蓋 App\Models\User 模型上的 sendPasswordResetNotification 方法。在此方法中,你可以使用任何你自建的 通知類 傳送通知。密碼重置 $token 是該方法接收的第一個引數。你可以使用此 $token 構建你選擇的密碼重置 URL,並將通知傳送給使用者。

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}