跳到主要內容

重設密碼

📝 此頁面為 Laravel 官方文檔的繁體中文翻譯。查看原始英文版本

重設密碼

簡介

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

[!NOTE] 想要快速開始?在全新的 Laravel 應用程式中安裝 Laravel 應用程式入門套件。Laravel 的入門套件將負責搭建整個認證系統,包括重設遺忘的密碼。

設定

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

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

  • database - 密碼重設資料儲存在關聯式資料庫中。
  • cache - 密碼重設資料儲存在您其中一個基於快取的儲存中。

驅動程式先決條件

資料庫

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

快取

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

'passwords' => [
    'users' => [
        'driver' => 'cache',
        'provider' => 'users',
        'store' => 'passwords', // 選擇性...
        'expire' => 60,
        'throttle' => 60,
    ],
],

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

設定受信任的主機

預設情況下,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::ResetLinkSent
        ? 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 語言檔中。

[!NOTE] 預設情況下,Laravel 應用程式骨架不包含 lang 目錄。如果您希望自訂 Laravel 的語言檔,可以透過 lang:publish Artisan 命令發佈它們。

您可能想知道 Laravel 如何在呼叫 Password facade 的 sendResetLink 方法時知道如何從應用程式的資料庫中檢索使用者記錄。Laravel 密碼代理使用認證系統的「使用者提供者」來檢索資料庫記錄。密碼代理使用的使用者提供者在 config/auth.php 設定檔的 passwords 設定陣列中配置。要了解更多關於編寫自訂使用者提供者的資訊,請參閱認證文件

[!NOTE] 手動實作密碼重設時,您需要自行定義視圖和路由的內容。如果您希望包含所有必要認證和驗證邏輯的腳手架,請查看 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::PasswordReset
        ? redirect()->route('login')->with('status', __($status))
        : back()->withErrors(['email' => [__($status)]]);
})->middleware('guest')->name('password.update');

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

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

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

在繼續之前,您可能想知道 Laravel 如何在呼叫 Password facade 的 reset 方法時知道如何從應用程式的資料庫中檢索使用者記錄。Laravel 密碼代理使用認證系統的「使用者提供者」來檢索資料庫記錄。密碼代理使用的使用者提供者在 config/auth.php 設定檔的 passwords 設定陣列中配置。要了解更多關於編寫自訂使用者提供者的資訊,請參閱認證文件

刪除過期令牌

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

php artisan auth:clear-resets

如果您希望自動化此過程,可以考慮將命令新增到應用程式的排程器

use Illuminate\Support\Facades\Schedule;

Schedule::command('auth:clear-resets')->everyFifteenMinutes();

自訂

重設連結自訂

您可以使用 ResetPassword 通知類別提供的 createUrlUsing 方法來自訂密碼重設連結 URL。此方法接受一個閉包,該閉包接收接收通知的使用者實例以及密碼重設連結令牌。通常,您應該從應用程式的 AppServiceProviderboot 方法中呼叫此方法:

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));
}