跳至內容

驗證

簡介

許多 Web 應用程式都提供使用者使用該應用程式進行驗證和「登入」的方法。在 Web 應用程式中實作此功能可能是一項複雜且有潛在風險的工作。因此,Laravel 致力於為您提供快速、安全且輕鬆實作驗證所需的工具。

Laravel 的驗證機制的核心由「守衛」和「提供者」組成。守衛定義如何針對每個請求驗證使用者。例如,Laravel 附帶一個 session 守衛,它使用 Session 儲存和 Cookie 維護狀態。

提供者定義如何從您的持久儲存中檢索使用者。Laravel 支援使用 Eloquent 和資料庫查詢建構器檢索使用者。但是,您可以根據應用程式的需要自由定義其他提供者。

您應用程式的驗證設定檔位於 config/auth.php。此檔案包含數個完善記錄的選項,可用於調整 Laravel 驗證服務的行為。

lightbulb

守衛和提供者不應與「角色」和「權限」混淆。若要深入瞭解如何透過權限授權使用者操作,請參閱授權文件。

入門套件

想要快速開始嗎?在新的 Laravel 應用程式中安裝Laravel 應用程式入門套件。遷移資料庫後,將您的瀏覽器導航至 /register 或任何其他已指派給您的應用程式的 URL。入門套件將負責搭建您整個驗證系統!

即使您選擇不在最終的 Laravel 應用程式中使用入門套件,安裝 Laravel Breeze 入門套件也是一個絕佳的機會,可以學習如何在實際的 Laravel 專案中實作 Laravel 的所有驗證功能。由於 Laravel Breeze 會為您建立驗證控制器、路由和視圖,因此您可以檢查這些檔案中的程式碼,以瞭解如何實作 Laravel 的驗證功能。

資料庫考量

預設情況下,Laravel 在您的 app/Models 目錄中包含一個 App\Models\User Eloquent 模型。此模型可用於預設的 Eloquent 驗證驅動程式。

如果您的應用程式未使用 Eloquent,您可以使用使用 Laravel 查詢建構器的 database 驗證提供者。如果您的應用程式使用 MongoDB,請查看 MongoDB 的官方Laravel 使用者驗證文件

在建構 App\Models\User 模型的資料庫架構時,請確保密碼欄位的長度至少為 60 個字元。當然,新的 Laravel 應用程式中包含的 users 資料表遷移已經建立了一個超過此長度的欄位。

此外,您應該驗證您的 users(或對等)資料表是否包含一個可為 null 的字串 remember_token 欄位,長度為 100 個字元。此欄位將用於儲存使用者在登入應用程式時選取「記住我」選項的權杖。同樣,新的 Laravel 應用程式中包含的預設 users 資料表遷移已經包含此欄位。

生態系統概述

Laravel 提供多個與驗證相關的套件。在繼續之前,我們將檢視 Laravel 中一般的驗證生態系統,並討論每個套件的預期用途。

首先,請考慮驗證如何運作。當使用 Web 瀏覽器時,使用者將透過登入表單提供其使用者名稱和密碼。如果這些認證正確,應用程式會將有關已驗證使用者的資訊儲存在使用者的Session中。發給瀏覽器的 Cookie 包含 Session ID,以便後續對應用程式的請求可以將使用者與正確的 Session 建立關聯。收到 Session Cookie 後,應用程式會根據 Session ID 檢索 Session 資料,並注意到驗證資訊已儲存在 Session 中,並將使用者視為「已驗證」。

當遠端服務需要驗證以存取 API 時,通常不會使用 Cookie 進行驗證,因為沒有 Web 瀏覽器。相反地,遠端服務會在每個請求中向 API 發送一個 API 權杖。應用程式可以根據有效 API 權杖的資料表驗證傳入的權杖,並將請求「驗證」為由與該 API 權杖相關聯的使用者執行。

Laravel 內建的瀏覽器驗證服務

Laravel 包含內建的驗證和 Session 服務,通常透過 AuthSession 門面存取。這些功能為從 Web 瀏覽器起始的請求提供基於 Cookie 的驗證。它們提供允許您驗證使用者認證和驗證使用者的方法。此外,這些服務會自動將正確的驗證資料儲存在使用者的 Session 中,並發出使用者的 Session Cookie。本文件中包含有關如何使用這些服務的討論。

應用程式入門套件

如本文件中所述,您可以手動與這些驗證服務互動,以建構應用程式自己的驗證層。但是,為了幫助您更快地開始,我們已發布免費套件,這些套件可提供整個驗證層的強大、現代化的支架。這些套件包括 Laravel BreezeLaravel JetstreamLaravel Fortify

Laravel Breeze 是所有 Laravel 驗證功能的簡單、最小實作,包括登入、註冊、密碼重設、電子郵件驗證和密碼確認。Laravel Breeze 的視圖層由使用 Tailwind CSS 設計樣式的簡單 Blade 模板組成。若要開始使用,請查看 Laravel 的應用程式入門套件上的文件。

Laravel Fortify 是 Laravel 的無頭驗證後端,實作了本文件中找到的許多功能,包括基於 Cookie 的驗證以及其他功能,如雙因素驗證和電子郵件驗證。Fortify 為 Laravel Jetstream 提供驗證後端,也可以與 Laravel Sanctum 結合獨立使用,以為需要使用 Laravel 驗證的 SPA 提供驗證。

Laravel Jetstream 是一個強大的應用程式起始套件,它使用並公開 Laravel Fortify 的驗證服務,並以 Tailwind CSSLivewire 和/或 Inertia 提供美觀現代的 UI。Laravel Jetstream 包含對雙因素驗證、團隊支援、瀏覽器會話管理、個人資料管理的可選支援,並與 Laravel Sanctum 內建整合,以提供 API 權杖驗證。Laravel 的 API 驗證方案將在下面討論。

Laravel 的 API 驗證服務

Laravel 提供了兩個可選的套件,以協助您管理 API 權杖並驗證使用 API 權杖發出的請求:PassportSanctum。請注意,這些函式庫和 Laravel 內建的基於 Cookie 的驗證函式庫並非互斥。這些函式庫主要專注於 API 權杖驗證,而內建的驗證服務則專注於基於 Cookie 的瀏覽器驗證。許多應用程式會同時使用 Laravel 內建的基於 Cookie 的驗證服務和 Laravel 的其中一個 API 驗證套件。

Passport

Passport 是一個 OAuth2 驗證提供者,提供各種 OAuth2「授權類型」,讓您可以發行各種權杖。一般來說,這是一個用於 API 驗證的強大且複雜的套件。但是,大多數應用程式不需要 OAuth2 規範提供的複雜功能,這可能會讓使用者和開發人員感到困惑。此外,開發人員過去一直對如何使用像 Passport 這樣的 OAuth2 驗證提供者來驗證 SPA 應用程式或行動應用程式感到困惑。

Sanctum

為了回應 OAuth2 的複雜性和開發人員的困惑,我們著手建構一個更簡單、更精簡的驗證套件,該套件可以處理來自網路瀏覽器的第一方網路請求和透過權杖進行的 API 請求。這個目標隨著 Laravel Sanctum 的發布而實現,對於將提供第一方網路 UI 以及 API 的應用程式,或是將由與後端 Laravel 應用程式分開存在的單頁應用程式 (SPA) 驅動的應用程式,或是提供行動用戶端的應用程式,應將其視為首選和推薦的驗證套件。

Laravel Sanctum 是一個混合式網路/API 驗證套件,可以管理您應用程式的整個驗證流程。之所以能夠如此,是因為當基於 Sanctum 的應用程式收到請求時,Sanctum 會先判斷請求是否包含引用已驗證會話的會話 Cookie。Sanctum 透過呼叫我們先前討論過的 Laravel 內建驗證服務來完成此操作。如果請求不是透過會話 Cookie 進行驗證,Sanctum 將檢查請求中是否有 API 權杖。如果存在 API 權杖,Sanctum 將使用該權杖驗證請求。若要深入了解此流程,請參閱 Sanctum 的「運作方式」文件。

我們選擇將 Laravel Sanctum 這個 API 套件包含在 Laravel Jetstream 應用程式起始套件中,因為我們認為它是最適合大多數網路應用程式驗證需求的套件。

摘要和選擇您的堆疊

總而言之,如果您的應用程式將透過瀏覽器存取,並且您正在建構單體式 Laravel 應用程式,則您的應用程式將使用 Laravel 內建的驗證服務。

接下來,如果您的應用程式提供將由第三方使用的 API,您將在 PassportSanctum 之間做出選擇,以提供您應用程式的 API 權杖驗證。一般來說,在可能的情況下,應該首選 Sanctum,因為它是一個簡單、完整的 API 驗證、SPA 驗證和行動驗證解決方案,包括支援「範圍」或「能力」。

如果您正在建構一個由 Laravel 後端驅動的單頁應用程式 (SPA),則應使用 Laravel Sanctum。當使用 Sanctum 時,您需要手動實作您自己的後端驗證路由,或使用 Laravel Fortify 作為無頭驗證後端服務,提供註冊、密碼重設、電子郵件驗證等功能的路由和控制器。

當您的應用程式絕對需要 OAuth2 規範提供的所有功能時,可以選擇 Passport。

而且,如果您想快速入門,我們很高興推薦 Laravel Breeze 作為一個快速啟動新 Laravel 應用程式的方法,該應用程式已使用我們首選的 Laravel 內建驗證服務和 Laravel Sanctum 驗證堆疊。

驗證快速入門

exclamation

此部分文件討論如何透過 Laravel 應用程式起始套件驗證使用者,其中包括 UI 支架,以協助您快速入門。如果您想直接與 Laravel 的驗證系統整合,請查看有關手動驗證使用者的文件。

安裝入門套件

首先,您應該安裝 Laravel 應用程式起始套件。我們目前的起始套件 Laravel Breeze 和 Laravel Jetstream 為將驗證整合到您新的 Laravel 應用程式中提供了精美設計的起點。

Laravel Breeze 是 Laravel 所有驗證功能(包括登入、註冊、密碼重設、電子郵件驗證和密碼確認)的最簡實作。Laravel Breeze 的檢視層由使用 Tailwind CSS 設定樣式的簡單 Blade 模板組成。此外,Breeze 還提供基於 LivewireInertia 的支架選項,並可選擇使用 Vue 或 React 作為基於 Inertia 的支架。

Laravel Jetstream 是一個更強大的應用程式起始套件,包括支援使用 LivewireInertia 和 Vue 為您的應用程式建立支架。此外,Jetstream 還具有對雙因素驗證、團隊、個人資料管理、瀏覽器會話管理、透過 Laravel Sanctum 提供的 API 支援、帳戶刪除等的可選支援。

檢索已驗證的使用者

在安裝驗證起始套件並允許使用者註冊並使用您的應用程式進行驗證之後,您通常需要與目前已驗證的使用者互動。在處理傳入的請求時,您可以透過 Auth 外觀的 user 方法存取已驗證的使用者。

use Illuminate\Support\Facades\Auth;
 
// Retrieve the currently authenticated user...
$user = Auth::user();
 
// Retrieve the currently authenticated user's ID...
$id = Auth::id();

或者,一旦使用者通過驗證,您可以透過 Illuminate\Http\Request 實例存取已驗證的使用者。請記住,類型提示類別將自動注入到您的控制器方法中。透過類型提示 Illuminate\Http\Request 物件,您可以透過請求的 user 方法方便地從應用程式中的任何控制器方法存取已驗證的使用者。

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
 
class FlightController extends Controller
{
/**
* Update the flight information for an existing flight.
*/
public function update(Request $request): RedirectResponse
{
$user = $request->user();
 
// ...
 
return redirect('/flights');
}
}

判斷目前使用者是否已驗證

若要判斷發出傳入 HTTP 請求的使用者是否已驗證,您可以使用 Auth 外觀上的 check 方法。如果使用者已驗證,此方法將傳回 true

use Illuminate\Support\Facades\Auth;
 
if (Auth::check()) {
// The user is logged in...
}
lightbulb

即使可以使用 check 方法判斷使用者是否已驗證,您通常會使用中介軟體來驗證使用者是否已驗證,然後才允許使用者存取某些路由/控制器。若要深入了解此內容,請查看有關保護路由的文件。

保護路由

路由中介軟體可用於僅允許已驗證的使用者存取給定的路由。Laravel 隨附 auth 中介軟體,它是 Illuminate\Auth\Middleware\Authenticate 類別的中介軟體別名。由於此中介軟體已由 Laravel 在內部建立別名,因此您只需將中介軟體附加到路由定義即可。

Route::get('/flights', function () {
// Only authenticated users may access this route...
})->middleware('auth');

重新導向未驗證的使用者

auth 中介軟體偵測到未驗證的使用者時,它會將使用者重新導向至 login 具名路由。您可以使用應用程式的 bootstrap/app.php 檔案中的 redirectGuestsTo 方法來修改此行為。

use Illuminate\Http\Request;
 
->withMiddleware(function (Middleware $middleware) {
$middleware->redirectGuestsTo('/login');
 
// Using a closure...
$middleware->redirectGuestsTo(fn (Request $request) => route('login'));
})

指定 Guard

auth 中介軟體附加到路由時,您也可以指定應使用哪個「Guard」來驗證使用者。指定的 Guard 應與 auth.php 組態檔案的 guards 陣列中的其中一個鍵對應。

Route::get('/flights', function () {
// Only authenticated users may access this route...
})->middleware('auth:admin');

登入節流

如果您使用的是 Laravel Breeze 或 Laravel Jetstream 起始套件,則速率限制將自動套用於登入嘗試。預設情況下,如果使用者在多次嘗試後未能提供正確的憑證,則使用者將在一分鐘內無法登入。此限制對於使用者的使用者名稱/電子郵件地址及其 IP 位址是唯一的。

lightbulb

如果您想限制應用程式中其他路由的速率,請查看速率限制文件

手動驗證使用者

您不需要使用 Laravel 的 應用程式起始套件中包含的驗證支架。如果您選擇不使用此支架,您需要直接使用 Laravel 驗證類別來管理使用者驗證。別擔心,這很容易!

我們將透過 Auth 外觀存取 Laravel 的驗證服務,因此我們需要確保在類別頂部匯入 Auth 外觀。接下來,讓我們看看 attempt 方法。attempt 方法通常用於處理來自應用程式「登入」表單的驗證嘗試。如果驗證成功,您應該重新產生使用者的會話,以防止會話固定

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
 
class LoginController extends Controller
{
/**
* Handle an authentication attempt.
*/
public function authenticate(Request $request): RedirectResponse
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
 
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
 
return redirect()->intended('dashboard');
}
 
return back()->withErrors([
'email' => 'The provided credentials do not match our records.',
])->onlyInput('email');
}
}

attempt 方法會接收一個鍵/值配對的陣列作為其第一個參數。陣列中的值將用於在您的資料庫表中尋找使用者。因此,在上面的範例中,將會透過 email 欄位的值來檢索使用者。如果找到使用者,資料庫中儲存的雜湊密碼將會與透過陣列傳遞給此方法的 password 值進行比較。您不應雜湊傳入請求的 password 值,因為框架會在與資料庫中的雜湊密碼進行比較之前自動雜湊該值。如果兩個雜湊密碼匹配,則會為使用者啟動一個已驗證的會話。

請記住,Laravel 的身份驗證服務將會根據您的身份驗證守衛的「提供者」配置,從您的資料庫中檢索使用者。在預設的 config/auth.php 設定檔中,指定了 Eloquent 使用者提供者,並且指示在檢索使用者時使用 App\Models\User 模型。您可以根據應用程式的需求在設定檔中變更這些值。

如果身份驗證成功,attempt 方法將會傳回 true。否則,將會傳回 false

Laravel 的重新導向器所提供的 intended 方法,會將使用者重新導向到他們在被身份驗證中介軟體攔截之前試圖存取的 URL。如果無法使用預期的目的地,則可以將回退 URI 提供給此方法。

指定額外的條件

如果您希望,除了使用者的電子郵件和密碼之外,您也可以在身份驗證查詢中新增額外的查詢條件。為此,我們可以簡單地將查詢條件新增到傳遞給 attempt 方法的陣列中。例如,我們可以驗證使用者是否標記為「啟用」。

if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// Authentication was successful...
}

對於複雜的查詢條件,您可以在憑證陣列中提供一個閉包。這個閉包將會以查詢實例來調用,允許您根據應用程式的需求自訂查詢。

use Illuminate\Database\Eloquent\Builder;
 
if (Auth::attempt([
'email' => $email,
'password' => $password,
fn (Builder $query) => $query->has('activeSubscription'),
])) {
// Authentication was successful...
}
exclamation

在這些範例中,email 不是必要選項,它僅用作範例。您應該使用與資料庫表格中的「使用者名稱」對應的任何欄位名稱。

attemptWhen 方法會接收一個閉包作為其第二個參數,可用於在實際驗證使用者之前,對潛在使用者執行更廣泛的檢查。閉包會接收潛在的使用者,並且應該傳回 truefalse,以指示是否可以驗證該使用者。

if (Auth::attemptWhen([
'email' => $email,
'password' => $password,
], function (User $user) {
return $user->isNotBanned();
})) {
// Authentication was successful...
}

存取特定的守衛實例

透過 Auth facade 的 guard 方法,您可以指定在驗證使用者時想要使用的守衛實例。這允許您使用完全獨立的可驗證模型或使用者表格來管理應用程式不同部分的身份驗證。

傳遞給 guard 方法的守衛名稱,應該與您的 auth.php 設定檔中設定的其中一個守衛相對應。

if (Auth::guard('admin')->attempt($credentials)) {
// ...
}

記住使用者

許多 Web 應用程式在其登入表單上提供「記住我」核取方塊。如果您想在應用程式中提供「記住我」功能,您可以將布林值作為第二個參數傳遞給 attempt 方法。

當此值為 true 時,Laravel 將無限期地保持使用者驗證狀態,直到他們手動登出為止。您的 users 表格必須包含字串 remember_token 欄位,該欄位將用於儲存「記住我」權杖。新 Laravel 應用程式隨附的 users 表格遷移已包含此欄位。

use Illuminate\Support\Facades\Auth;
 
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// The user is being remembered...
}

如果您的應用程式提供「記住我」功能,您可以使用 viaRemember 方法來判斷目前已驗證的使用者是否是使用「記住我」Cookie 進行驗證的。

use Illuminate\Support\Facades\Auth;
 
if (Auth::viaRemember()) {
// ...
}

其他驗證方法

驗證使用者實例

如果您需要將現有的使用者實例設定為目前已驗證的使用者,您可以將該使用者實例傳遞給 Auth facade 的 login 方法。給定的使用者實例必須是 Illuminate\Contracts\Auth\Authenticatable 合約 的實作。Laravel 隨附的 App\Models\User 模型已實作此介面。當您已經有一個有效的使用者實例時,例如在使用者向您的應用程式註冊後立即使用,此身份驗證方法非常有用。

use Illuminate\Support\Facades\Auth;
 
Auth::login($user);

您可以將布林值作為第二個參數傳遞給 login 方法。此值表示是否希望對已驗證的會話使用「記住我」功能。請記住,這表示會話將無限期地驗證,直到使用者手動登出應用程式為止。

Auth::login($user, $remember = true);

如果需要,您可以在呼叫 login 方法之前指定身份驗證守衛。

Auth::guard('admin')->login($user);

通過 ID 驗證使用者

若要使用使用者資料庫記錄的主鍵來驗證使用者,您可以使用 loginUsingId 方法。此方法接受您想要驗證的使用者的主鍵。

Auth::loginUsingId(1);

您可以將布林值傳遞給 loginUsingId 方法的 remember 參數。此值表示是否希望對已驗證的會話使用「記住我」功能。請記住,這表示會話將無限期地驗證,直到使用者手動登出應用程式為止。

Auth::loginUsingId(1, remember: true);

單次驗證使用者

您可以使用 once 方法來驗證使用者對於單次請求的應用程式。呼叫此方法時不會使用任何會話或 Cookie。

if (Auth::once($credentials)) {
// ...
}

HTTP 基本驗證

HTTP 基本身份驗證提供了一種快速驗證應用程式使用者的方式,而無需設定專用的「登入」頁面。若要開始,請將 auth.basic 中介軟體附加到路由。auth.basic 中介軟體包含在 Laravel 框架中,因此您不需要定義它。

Route::get('/profile', function () {
// Only authenticated users may access this route...
})->middleware('auth.basic');

將中介軟體附加到路由後,當您在瀏覽器中存取路由時,將會自動提示您輸入憑證。預設情況下,auth.basic 中介軟體會假設您 users 資料庫表格中的 email 欄位是使用者的「使用者名稱」。

關於 FastCGI 的注意事項

如果您使用 PHP FastCGI 和 Apache 來服務您的 Laravel 應用程式,HTTP 基本身份驗證可能無法正常運作。若要修正這些問題,可以將以下幾行新增至應用程式的 .htaccess 檔案中。

RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

無狀態 HTTP 基本驗證

您也可以在不於會話中設定使用者識別碼 Cookie 的情況下使用 HTTP 基本身份驗證。如果您選擇使用 HTTP 驗證來驗證對應用程式 API 的請求,這會很有幫助。為此,定義一個中介軟體,該中介軟體會呼叫 onceBasic 方法。如果 onceBasic 方法未傳回任何回應,則請求可能會進一步傳遞到應用程式中。

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
 
class AuthenticateOnceWithBasicAuth
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
return Auth::onceBasic() ?: $next($request);
}
 
}

接下來,將中介軟體附加到路由。

Route::get('/api/user', function () {
// Only authenticated users may access this route...
})->middleware(AuthenticateOnceWithBasicAuth::class);

登出

若要手動將使用者登出應用程式,您可以使用 Auth facade 提供的 logout 方法。這將會從使用者的會話中移除身份驗證資訊,以便後續的請求不會進行身份驗證。

除了呼叫 logout 方法之外,建議您使使用者的會話失效並重新產生其 CSRF 權杖。在將使用者登出後,您通常會將使用者重新導向到應用程式的根目錄。

use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
 
/**
* Log the user out of the application.
*/
public function logout(Request $request): RedirectResponse
{
Auth::logout();
 
$request->session()->invalidate();
 
$request->session()->regenerateToken();
 
return redirect('/');
}

使其他裝置上的 Session 無效

Laravel 還提供了一種機制,用於使使用者在其他裝置上處於活動狀態的會話失效和「登出」,而不會使其目前裝置上的會話失效。當使用者變更或更新其密碼,並且您希望使其他裝置上的會話失效,同時保持目前裝置的身份驗證時,通常會使用此功能。

在開始之前,您應該確保 Illuminate\Session\Middleware\AuthenticateSession 中介軟體包含在應該接收會話驗證的路由中。通常,您應該將此中介軟體放在路由群組定義中,以便可以將其應用於應用程式的大多數路由。預設情況下,可以使用 auth.session 中介軟體別名AuthenticateSession 中介軟體附加到路由。

Route::middleware(['auth', 'auth.session'])->group(function () {
Route::get('/', function () {
// ...
});
});

然後,您可以使用 Auth facade 提供的 logoutOtherDevices 方法。此方法要求使用者確認其目前密碼,您的應用程式應該透過輸入表單接受該密碼。

use Illuminate\Support\Facades\Auth;
 
Auth::logoutOtherDevices($currentPassword);

當呼叫 logoutOtherDevices 方法時,使用者的其他會話將完全失效,這表示他們將會「登出」先前驗證過的所有守衛。

密碼確認

在建置應用程式時,您偶爾會有應該要求使用者在執行動作之前或將使用者重新導向到應用程式的敏感區域之前確認密碼的動作。Laravel 包含內建的中介軟體,可讓此過程變得輕而易舉。實作此功能需要您定義兩個路由:一個路由顯示要求使用者確認密碼的視圖,另一個路由確認密碼有效,並將使用者重新導向到他們預期的目的地。

lightbulb

以下文件討論如何直接與 Laravel 的密碼確認功能整合;但是,如果您想更快地開始,Laravel 應用程式入門套件包含對此功能的支持!

設定

在確認密碼後,使用者在三個小時內不會再次被要求確認其密碼。但是,您可以透過變更應用程式 config/auth.php 設定檔中 password_timeout 設定值,來設定在重新提示使用者輸入密碼之前經過的時間長度。

路由

密碼確認表單

首先,我們將定義一個路由,以顯示一個要求使用者確認其密碼的視圖。

Route::get('/confirm-password', function () {
return view('auth.confirm-password');
})->middleware('auth')->name('password.confirm');

正如您可能預期的那樣,此路由傳回的視圖應該包含一個包含 password 欄位的表單。此外,請隨意在視圖中包含文字,以解釋使用者正在進入應用程式的受保護區域,並且必須確認其密碼。

確認密碼

接下來,我們將定義一個路由,用於處理來自「確認密碼」視圖的表單請求。此路由將負責驗證密碼,並將使用者重新導向到他們預期的目的地。

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Redirect;
 
Route::post('/confirm-password', function (Request $request) {
if (! Hash::check($request->password, $request->user()->password)) {
return back()->withErrors([
'password' => ['The provided password does not match our records.']
]);
}
 
$request->session()->passwordConfirmed();
 
return redirect()->intended();
})->middleware(['auth', 'throttle:6,1']);

在繼續之前,讓我們更詳細地檢視此路由。首先,判斷請求的 password 欄位是否確實與已驗證的使用者的密碼相符。如果密碼有效,我們需要告知 Laravel 的會話使用者已確認其密碼。passwordConfirmed 方法將在使用者的會話中設定時間戳記,Laravel 可以使用該時間戳記來判斷使用者上次確認其密碼的時間。最後,我們可以將使用者重新導向到他們預期的目的地。

保護路由

您應該確保將執行需要最近密碼確認動作的任何路由指派給 password.confirm 中介軟體。此中介軟體包含在 Laravel 的預設安裝中,並且會自動將使用者預期的目的地儲存在會話中,以便在使用者確認密碼後,可以將使用者重新導向到該位置。將使用者預期的目的地儲存在會話中之後,中介軟體會將使用者重新導向到 password.confirm 命名路由

Route::get('/settings', function () {
// ...
})->middleware(['password.confirm']);
 
Route::post('/settings', function () {
// ...
})->middleware(['password.confirm']);

新增自訂守衛

您可以使用 Auth facade 上的 extend 方法定義自己的身份驗證守衛。您應該將呼叫 extend 方法放在 服務提供者中。由於 Laravel 已經附帶 AppServiceProvider,我們可以將程式碼放在該提供者中。

<?php
 
namespace App\Providers;
 
use App\Services\Auth\JwtGuard;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider;
 
class AppServiceProvider extends ServiceProvider
{
// ...
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Auth::extend('jwt', function (Application $app, string $name, array $config) {
// Return an instance of Illuminate\Contracts\Auth\Guard...
 
return new JwtGuard(Auth::createUserProvider($config['provider']));
});
}
}

如上面的範例所示,傳遞給 extend 方法的回呼應該傳回 Illuminate\Contracts\Auth\Guard 的實作。此介面包含一些您需要實作的方法,以定義自訂守衛。定義自訂守衛後,您可以在 auth.php 設定檔的 guards 設定中參考該守衛。

'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],

閉包請求守衛

實作自訂、基於 HTTP 請求的身份驗證系統最簡單的方法是使用 Auth::viaRequest 方法。此方法允許您使用單個閉包快速定義身份驗證流程。

若要開始,請在應用程式的 AppServiceProviderboot 方法中呼叫 Auth::viaRequest 方法。viaRequest 方法的第一個參數會接收一個身份驗證驅動程式名稱。這個名稱可以是任何描述自訂保護機制的字串。傳遞給這個方法的第二個參數應該是一個閉包,它會接收傳入的 HTTP 請求,並返回一個使用者實例,或者如果身份驗證失敗,則返回 null

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Auth::viaRequest('custom-token', function (Request $request) {
return User::where('token', (string) $request->token)->first();
});
}

一旦定義了自訂身份驗證驅動程式,您就可以將其設定為 auth.php 設定檔中 guards 設定內的驅動程式。

'guards' => [
'api' => [
'driver' => 'custom-token',
],
],

最後,您可以在將身份驗證中介軟體指派給路由時,參考這個保護機制。

Route::middleware('auth:api')->group(function () {
// ...
});

新增自訂使用者提供者

如果您不使用傳統的關聯式資料庫來儲存使用者,您將需要使用您自己的身份驗證使用者提供器來擴充 Laravel。我們將在 Auth facade 上使用 provider 方法來定義自訂使用者提供器。使用者提供器解析器應返回 Illuminate\Contracts\Auth\UserProvider 的實作。

<?php
 
namespace App\Providers;
 
use App\Extensions\MongoUserProvider;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider;
 
class AppServiceProvider extends ServiceProvider
{
// ...
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Auth::provider('mongo', function (Application $app, array $config) {
// Return an instance of Illuminate\Contracts\Auth\UserProvider...
 
return new MongoUserProvider($app->make('mongo.connection'));
});
}
}

在使用 provider 方法註冊提供器之後,您可以在 auth.php 設定檔中切換到新的使用者提供器。首先,定義一個使用您新驅動程式的 provider

'providers' => [
'users' => [
'driver' => 'mongo',
],
],

最後,您可以在您的 guards 設定中參考這個提供器。

'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],

使用者提供者合約

Illuminate\Contracts\Auth\UserProvider 的實作負責從持久儲存系統(例如 MySQL、MongoDB 等)中取得 Illuminate\Contracts\Auth\Authenticatable 的實作。這兩個介面允許 Laravel 身份驗證機制繼續運作,無論使用者資料如何儲存或使用哪種類型的類別來表示已驗證的使用者。

讓我們看一下 Illuminate\Contracts\Auth\UserProvider 契約。

<?php
 
namespace Illuminate\Contracts\Auth;
 
interface UserProvider
{
public function retrieveById($identifier);
public function retrieveByToken($identifier, $token);
public function updateRememberToken(Authenticatable $user, $token);
public function retrieveByCredentials(array $credentials);
public function validateCredentials(Authenticatable $user, array $credentials);
public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false);
}

retrieveById 函式通常會接收一個代表使用者的鍵,例如 MySQL 資料庫中的自動遞增 ID。與該 ID 相符的 Authenticatable 實作應由該方法檢索並返回。

retrieveByToken 函式透過它們唯一的 $identifier 和「記住我」$token 來檢索使用者,通常儲存在像 remember_token 這樣的資料庫欄位中。與前一個方法一樣,這個方法應該返回具有相符權杖值的 Authenticatable 實作。

updateRememberToken 方法使用新的 $token 更新 $user 實例的 remember_token。在成功「記住我」身份驗證嘗試或使用者登出時,會為使用者指派一個新的權杖。

retrieveByCredentials 方法接收在嘗試使用應用程式進行身份驗證時傳遞給 Auth::attempt 方法的憑證陣列。然後,該方法應「查詢」底層持久儲存,以尋找與這些憑證相符的使用者。通常,此方法將執行一個帶有「where」條件的查詢,該條件搜尋具有與 $credentials['username'] 值相符的「username」的使用者記錄。該方法應返回 Authenticatable 的實作。此方法不應嘗試進行任何密碼驗證或身份驗證。

validateCredentials 方法應將給定的 $user$credentials 進行比較,以驗證使用者身份。例如,此方法通常會使用 Hash::check 方法來比較 $user->getAuthPassword() 的值與 $credentials['password'] 的值。此方法應返回 truefalse,表示密碼是否有效。

如果需要並支援,rehashPasswordIfRequired 方法應該重新雜湊給定 $user 的密碼。例如,此方法通常會使用 Hash::needsRehash 方法來確定 $credentials['password'] 的值是否需要重新雜湊。如果密碼需要重新雜湊,該方法應該使用 Hash::make 方法重新雜湊密碼,並更新底層持久儲存中的使用者記錄。

可驗證合約

現在我們已經探討了 UserProvider 上的每個方法,讓我們看一下 Authenticatable 契約。請記住,使用者提供器應從 retrieveByIdretrieveByTokenretrieveByCredentials 方法返回此介面的實作。

<?php
 
namespace Illuminate\Contracts\Auth;
 
interface Authenticatable
{
public function getAuthIdentifierName();
public function getAuthIdentifier();
public function getAuthPasswordName();
public function getAuthPassword();
public function getRememberToken();
public function setRememberToken($value);
public function getRememberTokenName();
}

此介面很簡單。getAuthIdentifierName 方法應該返回使用者的「主鍵」欄位的名稱,而 getAuthIdentifier 方法應該返回使用者的「主鍵」。當使用 MySQL 後端時,這很可能是指派給使用者記錄的自動遞增主鍵。getAuthPasswordName 方法應該返回使用者密碼欄位的名稱。getAuthPassword 方法應該返回使用者雜湊後的密碼。

這個介面允許身份驗證系統與任何「使用者」類別一起運作,無論您使用哪種 ORM 或儲存抽象層。預設情況下,Laravel 在 app/Models 目錄中包含一個 App\Models\User 類別,它實作了此介面。

自動重新雜湊密碼

Laravel 的預設密碼雜湊演算法是 bcrypt。bcrypt 雜湊的「工作因子」可以透過應用程式的 config/hashing.php 設定檔或 BCRYPT_ROUNDS 環境變數進行調整。

通常,隨著 CPU/GPU 處理能力的提高,bcrypt 工作因子應隨著時間推移而增加。如果您為您的應用程式增加 bcrypt 工作因子,當使用者透過 Laravel 的入門套件驗證您的應用程式時,或者當您透過 attempt 方法 手動驗證使用者 時,Laravel 將會優雅且自動地重新雜湊使用者密碼。

通常,自動密碼重新雜湊不應中斷您的應用程式;但是,您可以透過發布 hashing 設定檔來停用此行為。

php artisan config:publish hashing

發布設定檔後,您可以將 rehash_on_login 設定值設為 false

'rehash_on_login' => false,

事件

Laravel 在身份驗證過程中會發送各種事件。您可以為以下任何事件定義監聽器

事件名稱
Illuminate\Auth\Events\Registered
Illuminate\Auth\Events\Attempting
Illuminate\Auth\Events\Authenticated
Illuminate\Auth\Events\Login
Illuminate\Auth\Events\Failed
Illuminate\Auth\Events\Validated
Illuminate\Auth\Events\Verified
Illuminate\Auth\Events\Logout
Illuminate\Auth\Events\CurrentDeviceLogout
Illuminate\Auth\Events\OtherDeviceLogout
Illuminate\Auth\Events\Lockout
Illuminate\Auth\Events\PasswordReset