跳至內容

重設密碼

簡介

大多數 Web 應用程式都提供讓使用者重設忘記密碼的方式。Laravel 沒有強迫您為建立的每個應用程式手動重新實作此功能,而是提供方便的服務來傳送密碼重設連結和安全地重設密碼。

lightbulb

想快速開始嗎?在全新的 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 的預設 0001_01_01_000000_create_users_table.php 資料庫遷移中。

設定信任主機

依預設,Laravel 會回應它收到的所有請求,無論 HTTP 請求的 Host 標頭內容為何。此外,在 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');

此路由傳回的視圖應有一個包含 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 外觀模式) 向使用者傳送密碼重設連結。密碼代理將負責根據給定的欄位(在此範例中為電子郵件地址)擷取使用者,並透過 Laravel 的內建 通知系統 向使用者傳送密碼重設連結。

sendResetLink 方法會傳回「狀態」slug。可以使用 Laravel 的 本地化 輔助函數來翻譯此狀態,以便向使用者顯示有關其請求狀態的使用者友善訊息。密碼重設狀態的翻譯由您應用程式的 lang/{lang}/passwords.php 語言檔案決定。每個可能的狀態 slug 值都有一個項目位於 passwords 語言檔案中。

lightbulb

依預設,Laravel 應用程式框架不包含 lang 目錄。如果您想要自訂 Laravel 的語言檔案,可以使用 lang:publish Artisan 命令發佈它們。

您可能想知道,當呼叫 Password 外觀模式的 sendResetLink 方法時,Laravel 如何知道如何從您應用程式的資料庫擷取使用者記錄。Laravel 密碼代理會使用您身份驗證系統的「使用者提供者」來擷取資料庫記錄。密碼代理使用的使用者提供者會在您的 config/auth.php 設定檔的 passwords 設定陣列中設定。若要深入瞭解如何撰寫自訂使用者提供者,請參閱 身份驗證文件

lightbulb

手動實作密碼重設時,您需要自行定義視圖和路由的內容。如果您想要包含所有必要身份驗證和驗證邏輯的架構,請查看 Laravel 應用程式入門套件

重設密碼

密碼重設表單

接下來,我們將定義當使用者按一下已透過電子郵件傳送給他們的密碼重設連結並提供新密碼後,實際重設密碼所需的路由。首先,讓我們定義當使用者按一下密碼重設連結時會顯示重設密碼表單的路由。此路由將接收一個 token 參數,稍後我們將使用該參數來驗證密碼重設請求

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

此路由傳回的視圖應顯示一個包含 email 欄位、password 欄位、password_confirmation 欄位和隱藏的 token 欄位的表單,其中應包含我們路由接收的秘密 $token 值。

處理表單提交

當然,我們需要定義一個路由來實際處理密碼重設表單提交。此路由將負責驗證傳入的請求,並更新資料庫中使用者的密碼

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::PASSWORD_RESET
? redirect()->route('login')->with('status', __($status))
: back()->withErrors(['email' => [__($status)]]);
})->middleware('guest')->name('password.update');

在繼續之前,讓我們更詳細地檢查此路由。首先,會驗證請求的 tokenemailpassword 屬性。接下來,我們將使用 Laravel 的內建「密碼代理」(透過 Password 外觀模式) 來驗證密碼重設請求憑證。

如果提供給密碼代理的令牌、電子郵件地址和密碼有效,則會叫用傳遞給 reset 方法的閉包。在此閉包中,該閉包會接收使用者執行個體和提供給密碼重設表單的純文字密碼,我們可以在資料庫中更新使用者的密碼。

reset 方法會傳回「狀態」slug。可以使用 Laravel 的 本地化 輔助函數來翻譯此狀態,以便向使用者顯示有關其請求狀態的使用者友善訊息。密碼重設狀態的翻譯由您應用程式的 lang/{lang}/passwords.php 語言檔案決定。每個可能的狀態 slug 值都有一個項目位於 passwords 語言檔案中。如果您的應用程式不包含 lang 目錄,您可以使用 lang:publish Artisan 命令來建立它。

在繼續之前,您可能會好奇 Laravel 在呼叫 Password 外觀的 reset 方法時,是如何從應用程式的資料庫中檢索使用者紀錄的。 Laravel 密碼仲介器利用您驗證系統的「使用者供應商」來檢索資料庫紀錄。密碼仲介器使用的使用者供應商是在您 config/auth.php 設定檔的 passwords 設定陣列中配置的。若要深入了解如何撰寫自訂使用者供應商,請參閱驗證文件

刪除過期令牌

已過期的密碼重設 Token 仍然會存在於您的資料庫中。不過,您可以使用 auth:clear-resets Artisan 指令輕鬆刪除這些紀錄。

php artisan auth:clear-resets

如果您想自動化此流程,請考慮將此指令加入您應用程式的排程器中。

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

客製化

您可以使用 ResetPassword 通知類別提供的 createUrlUsing 方法來自訂密碼重設連結 URL。此方法接受一個閉包,該閉包會接收正在接收通知的使用者實例以及密碼重設連結 Token。通常,您應該從您的 App\Providers\AppServiceProvider 服務提供者的 boot 方法中呼叫此方法。

use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
 
/**
* Bootstrap any application services.
*/
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;
 
/**
* Send a password reset notification to the user.
*
* @param string $token
*/
public function sendPasswordResetNotification($token): void
{
$url = 'https://example.com/reset-password?token='.$token;
 
$this->notify(new ResetPasswordNotification($url));
}