跳到內容

驗證

介紹

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

在其核心,Laravel 的驗證工具由「Guard」和「Provider」組成。 Guard 定義了如何驗證每個請求的使用者身分。例如,Laravel 附帶一個 session Guard,它使用 Session 儲存和 Cookie 維護狀態。

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

您應用程式的驗證設定檔位於 config/auth.php。此檔案包含多個有詳細說明的選項,用於調整 Laravel 驗證服務的行為。

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

入門套件

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

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

資料庫考量

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

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

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

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

生態系統概述

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

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

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

Laravel 的內建瀏覽器驗證服務

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

應用程式入門套件

如本文件所述,您可以手動與這些驗證服務互動,以建構應用程式自己的驗證層。但是,為了幫助您更快開始,我們發佈了 免費入門套件,這些套件提供整個驗證層的強大、現代化的基礎結構。

Laravel 的 API 驗證服務

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

Passport

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

Sanctum

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

Laravel Sanctum 是一個混合 Web / API 驗證套件,可以管理應用程式的整個驗證流程。這是可能的,因為當基於 Sanctum 的應用程式收到請求時,Sanctum 將首先確定請求是否包含引用已驗證 Session 的 Session Cookie。Sanctum 透過呼叫我們稍早討論的 Laravel 內建驗證服務來完成此操作。如果請求未透過 Session Cookie 驗證,Sanctum 將檢查請求中是否存在 API Token。如果存在 API Token,Sanctum 將使用該 Token 驗證請求。若要深入瞭解此流程,請參閱 Sanctum 的 「運作方式」 文件。

摘要和選擇您的堆疊

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

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

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

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

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

驗證快速入門

本文件的此部分討論如何透過 Laravel 應用程式入門套件 驗證使用者身分,其中包括 UI 基礎結構,以幫助您快速開始。如果您想直接與 Laravel 的驗證系統整合,請查看關於 手動驗證使用者 的文件。

安裝入門套件

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

取得已驗證的使用者

從入門套件建立應用程式並允許使用者註冊和驗證您的應用程式後,您通常需要與目前已驗證的使用者互動。在處理傳入的請求時,您可以透過 Auth Facade 的 user 方法存取已驗證的使用者

1use Illuminate\Support\Facades\Auth;
2 
3// Retrieve the currently authenticated user...
4$user = Auth::user();
5 
6// Retrieve the currently authenticated user's ID...
7$id = Auth::id();

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

1<?php
2 
3namespace App\Http\Controllers;
4 
5use Illuminate\Http\RedirectResponse;
6use Illuminate\Http\Request;
7 
8class FlightController extends Controller
9{
10 /**
11 * Update the flight information for an existing flight.
12 */
13 public function update(Request $request): RedirectResponse
14 {
15 $user = $request->user();
16 
17 // ...
18 
19 return redirect('/flights');
20 }
21}

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

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

1use Illuminate\Support\Facades\Auth;
2 
3if (Auth::check()) {
4 // The user is logged in...
5}

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

保護路由

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

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

重新導向未驗證的使用者

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

1use Illuminate\Http\Request;
2 
3->withMiddleware(function (Middleware $middleware) {
4 $middleware->redirectGuestsTo('/login');
5 
6 // Using a closure...
7 $middleware->redirectGuestsTo(fn (Request $request) => route('login'));
8})

指定 Guard

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

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

登入請求節流

如果您正在使用我們的 應用程式入門套件 之一,速率限制將自動應用於登入嘗試。預設情況下,如果使用者在多次嘗試後未能提供正確的憑證,則使用者將在一分鐘內無法登入。請求節流對於使用者的使用者名稱/電子郵件地址及其 IP 位址是唯一的。

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

手動驗證使用者

您不需要使用 Laravel 的 應用程式入門套件 中包含的驗證基礎結構。如果您選擇不使用此基礎結構,則需要直接使用 Laravel 驗證類別來管理使用者驗證。別擔心,這很容易!

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

1<?php
2 
3namespace App\Http\Controllers;
4 
5use Illuminate\Http\Request;
6use Illuminate\Http\RedirectResponse;
7use Illuminate\Support\Facades\Auth;
8 
9class LoginController extends Controller
10{
11 /**
12 * Handle an authentication attempt.
13 */
14 public function authenticate(Request $request): RedirectResponse
15 {
16 $credentials = $request->validate([
17 'email' => ['required', 'email'],
18 'password' => ['required'],
19 ]);
20 
21 if (Auth::attempt($credentials)) {
22 $request->session()->regenerate();
23 
24 return redirect()->intended('dashboard');
25 }
26 
27 return back()->withErrors([
28 'email' => 'The provided credentials do not match our records.',
29 ])->onlyInput('email');
30 }
31}

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

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

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

Laravel 重新導向器提供的 intended 方法會將使用者重新導向到他們在被驗證中介層攔截之前嘗試存取的 URL。如果預期的目的地不可用,可以為此方法提供備用 URI。

指定其他條件

如果您願意,除了使用者的電子郵件和密碼之外,您也可以將額外的查詢條件新增至驗證查詢。若要完成此操作,我們可以簡單地將查詢條件新增至傳遞給 attempt 方法的陣列。例如,我們可以驗證使用者是否標記為「active」

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

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

1use Illuminate\Database\Eloquent\Builder;
2 
3if (Auth::attempt([
4 'email' => $email,
5 'password' => $password,
6 fn (Builder $query) => $query->has('activeSubscription'),
7])) {
8 // Authentication was successful...
9}

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

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

1if (Auth::attemptWhen([
2 'email' => $email,
3 'password' => $password,
4], function (User $user) {
5 return $user->isNotBanned();
6})) {
7 // Authentication was successful...
8}

存取特定的 Guard 實例

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

傳遞給 guard 方法的 Guard 名稱應對應於 auth.php 設定檔中設定的其中一個 Guard

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

記住使用者

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

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

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

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

1use Illuminate\Support\Facades\Auth;
2 
3if (Auth::viaRemember()) {
4 // ...
5}

其他驗證方法

驗證使用者實例

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

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

您可以將布林值作為第二個引數傳遞給 login 方法。此值指示是否需要為已驗證的 Session 提供「記住我」功能。請記住,這表示 Session 將無限期地驗證,或直到使用者手動登出應用程式

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

如果需要,您可以在呼叫 login 方法之前指定驗證 Guard

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

依 ID 驗證使用者

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

1Auth::loginUsingId(1);

您可以將布林值傳遞給 loginUsingId 方法的 remember 引數。此值指示是否需要為已驗證的 Session 提供「記住我」功能。請記住,這表示 Session 將無限期地驗證,或直到使用者手動登出應用程式

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

單次驗證使用者

您可以使用 once 方法來驗證使用者對應用程式的單個請求。呼叫此方法時,不會使用 Session 或 Cookie

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

HTTP 基本驗證

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

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

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

關於 FastCGI 的注意事項

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

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

無狀態 HTTP 基本驗證

您也可以使用 HTTP 基本身分驗證,而無需在 session 工作階段中設定使用者識別 Cookie。如果您選擇使用 HTTP 驗證來驗證對應用程式 API 的請求,這會特別有用。若要完成此操作,請定義一個中介軟體,呼叫 onceBasic 方法。如果 onceBasic 方法沒有傳回任何回應,則請求可能會進一步傳遞到應用程式中。

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Illuminate\Support\Facades\Auth;
8use Symfony\Component\HttpFoundation\Response;
9 
10class AuthenticateOnceWithBasicAuth
11{
12 /**
13 * Handle an incoming request.
14 *
15 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
16 */
17 public function handle(Request $request, Closure $next): Response
18 {
19 return Auth::onceBasic() ?: $next($request);
20 }
21 
22}

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

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

登出

若要手動將使用者登出您的應用程式,您可以使用 Auth facade 提供的 logout 方法。這將從使用者的 session 工作階段中移除身分驗證資訊,以便後續請求不會通過身分驗證。

除了呼叫 logout 方法之外,建議您使使用者的 session 工作階段失效,並重新產生他們的 CSRF 令牌。在使用者登出後,您通常會將使用者重新導向到應用程式的根目錄。

1use Illuminate\Http\Request;
2use Illuminate\Http\RedirectResponse;
3use Illuminate\Support\Facades\Auth;
4 
5/**
6 * Log the user out of the application.
7 */
8public function logout(Request $request): RedirectResponse
9{
10 Auth::logout();
11 
12 $request->session()->invalidate();
13 
14 $request->session()->regenerateToken();
15 
16 return redirect('/');
17}

使其他裝置上的 Session 失效

Laravel 也提供一種機制,可以使使用者在其他裝置上活動的 session 工作階段失效並「登出」,而不會使他們目前裝置上的 session 工作階段失效。當使用者變更或更新密碼,並且您想要使其他裝置上的 session 工作階段失效,同時保持目前裝置已驗證的狀態時,通常會使用此功能。

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

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

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

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

logoutOtherDevices 方法被調用時,使用者的其他 session 工作階段將完全失效,這表示他們將從先前通過身分驗證的所有 guards 中「登出」。

密碼確認

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

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

設定

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

路由

密碼確認表單

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

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

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

確認密碼

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

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

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

保護路由

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

1Route::get('/settings', function () {
2 // ...
3})->middleware(['password.confirm']);
4 
5Route::post('/settings', function () {
6 // ...
7})->middleware(['password.confirm']);

新增自訂 Guard

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

1<?php
2 
3namespace App\Providers;
4 
5use App\Services\Auth\JwtGuard;
6use Illuminate\Contracts\Foundation\Application;
7use Illuminate\Support\Facades\Auth;
8use Illuminate\Support\ServiceProvider;
9 
10class AppServiceProvider extends ServiceProvider
11{
12 // ...
13 
14 /**
15 * Bootstrap any application services.
16 */
17 public function boot(): void
18 {
19 Auth::extend('jwt', function (Application $app, string $name, array $config) {
20 // Return an instance of Illuminate\Contracts\Auth\Guard...
21 
22 return new JwtGuard(Auth::createUserProvider($config['provider']));
23 });
24 }
25}

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

1'guards' => [
2 'api' => [
3 'driver' => 'jwt',
4 'provider' => 'users',
5 ],
6],

Closure 請求 Guard

實作基於 HTTP 請求的自訂身分驗證系統最簡單的方法是使用 Auth::viaRequest 方法。此方法允許您使用單一 closure 快速定義您的身分驗證過程。

若要開始,請在應用程式 AppServiceProviderboot 方法中呼叫 Auth::viaRequest 方法。viaRequest 方法接受身分驗證驅動程式名稱作為其第一個引數。此名稱可以是描述您的自訂 guard 的任何字串。傳遞給此方法的第二個引數應為一個 closure,該 closure 接收傳入的 HTTP 請求,並傳回使用者實例,或者如果身分驗證失敗,則傳回 null

1use App\Models\User;
2use Illuminate\Http\Request;
3use Illuminate\Support\Facades\Auth;
4 
5/**
6 * Bootstrap any application services.
7 */
8public function boot(): void
9{
10 Auth::viaRequest('custom-token', function (Request $request) {
11 return User::where('token', (string) $request->token)->first();
12 });
13}

一旦您的自訂身分驗證驅動程式被定義,您可以在 auth.php 組態檔的 guards 組態中將其組態為驅動程式。

1'guards' => [
2 'api' => [
3 'driver' => 'custom-token',
4 ],
5],

最後,您可以在將身分驗證中介軟體指派給路由時參考該 guard。

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

新增自訂使用者提供器

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

1<?php
2 
3namespace App\Providers;
4 
5use App\Extensions\MongoUserProvider;
6use Illuminate\Contracts\Foundation\Application;
7use Illuminate\Support\Facades\Auth;
8use Illuminate\Support\ServiceProvider;
9 
10class AppServiceProvider extends ServiceProvider
11{
12 // ...
13 
14 /**
15 * Bootstrap any application services.
16 */
17 public function boot(): void
18 {
19 Auth::provider('mongo', function (Application $app, array $config) {
20 // Return an instance of Illuminate\Contracts\Auth\UserProvider...
21 
22 return new MongoUserProvider($app->make('mongo.connection'));
23 });
24 }
25}

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

1'providers' => [
2 'users' => [
3 'driver' => 'mongo',
4 ],
5],

最後,您可以在 guards 組態中參考此提供者。

1'guards' => [
2 'web' => [
3 'driver' => 'session',
4 'provider' => 'users',
5 ],
6],

使用者提供器契約

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

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

1<?php
2 
3namespace Illuminate\Contracts\Auth;
4 
5interface UserProvider
6{
7 public function retrieveById($identifier);
8 public function retrieveByToken($identifier, $token);
9 public function updateRememberToken(Authenticatable $user, $token);
10 public function retrieveByCredentials(array $credentials);
11 public function validateCredentials(Authenticatable $user, array $credentials);
12 public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false);
13}

retrieveById 函數通常接收代表使用者的 key,例如來自 MySQL 資料庫的自動遞增 ID。與該 ID 相符的 Authenticatable 實作應由此方法擷取並傳回。

retrieveByToken 函數透過使用者唯一的 $identifier 和「記住我」$token (通常儲存在資料庫欄位 (例如 remember_token) 中) 擷取使用者。與前一個方法一樣,應由此方法傳回具有相符 token 值的 Authenticatable 實作。

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

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

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

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

可驗證身分契約

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

1<?php
2 
3namespace Illuminate\Contracts\Auth;
4 
5interface Authenticatable
6{
7 public function getAuthIdentifierName();
8 public function getAuthIdentifier();
9 public function getAuthPasswordName();
10 public function getAuthPassword();
11 public function getRememberToken();
12 public function setRememberToken($value);
13 public function getRememberTokenName();
14}

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

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

自動密碼重新雜湊

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

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

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

1php artisan config:publish hashing

一旦組態檔已發布,您可以將 rehash_on_login 組態值設定為 false

1'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

Laravel 是最具生產力的方式,能夠
建置、部署和監控軟體。