簡介 (Introduction)
許多 Web 應用程式提供一種方式讓使用者透過應用程式進行認證並「登入」。在 Web 應用程式中實作此功能可能是一項複雜且具有潛在風險的工作。因此,Laravel 致力於為您提供快速、安全且輕鬆實作認證所需的工具。
Laravel 的認證機制核心由「guards」和「providers」組成。Guards 定義了每個請求如何認證使用者。例如,Laravel 內建的 session guard 使用 Session 儲存和 cookies 來維護狀態。
Providers 定義了如何從您的持久性儲存中取得使用者。Laravel 內建支援使用 Eloquent 和資料庫查詢建構器來取得使用者。但是,您可以根據應用程式的需要自由定義其他 providers。
您的應用程式的認證設定檔位於 config/auth.php。此檔案包含數個有詳細文件說明的選項,用於調整 Laravel 認證服務的行為。
[!NOTE] Guards 和 providers 不應與「角色」和「權限」混淆。若要了解更多關於透過權限授權使用者動作的資訊,請參閱 授權 文件。
Starter Kits
想要快速開始嗎?在新的 Laravel 應用程式中安裝 Laravel 應用程式 Starter Kit。在遷移資料庫後,將瀏覽器導航到 /register 或任何分配給您的應用程式的其他 URL。Starter kits 將負責搭建您整個認證系統的鷹架!
即使您選擇在最終的 Laravel 應用程式中不使用 starter kit,安裝 starter kit 也是學習如何在實際 Laravel 專案中實作所有 Laravel 認證功能的絕佳機會。 由於 Laravel starter kits 包含認證 controllers、routes 和 views,您可以檢查這些檔案中的程式碼,了解如何實作 Laravel 的認證功能。
資料庫注意事項 (Introduction Database Considerations)
預設情況下,Laravel 在您的 app/Models 目錄中包含一個 App\Models\User Eloquent model。此 model 可以與預設的 Eloquent 認證驅動程式一起使用。
如果您的應用程式不使用 Eloquent,您可以使用 database 認證 provider,它使用 Laravel 查詢建構器。如果您的應用程式使用 MongoDB,請查看 MongoDB 官方的 Laravel 使用者認證文件。
在為 App\Models\User model 建立資料庫結構描述時,請確保密碼欄位長度至少為 60 個字元。當然,新 Laravel 應用程式中包含的 users 資料表 migration 已經建立了超過此長度的欄位。
此外,您應該驗證您的 users(或等效)資料表包含一個可為 null 的、100 個字元的字串 remember_token 欄位。此欄位將用於為在登入應用程式時選擇「記住我」選項的使用者儲存 token。同樣,新 Laravel 應用程式中包含的預設 users 資料表 migration 已經包含此欄位。
生態系統概述 (Ecosystem Overview)
Laravel 提供多個與認證相關的套件。在繼續之前,我們將回顧 Laravel 中的一般認證生態系統,並討論每個套件的預期用途。
首先,考慮認證是如何運作的。當使用 Web 瀏覽器時,使用者將透過登入表單提供其使用者名稱和密碼。如果這些憑證正確,應用程式將在使用者的 Session 中儲存有關已認證使用者的資訊。發送給瀏覽器的 cookie 包含 Session ID,以便對應用程式的後續請求可以將使用者與正確的 Session 關聯起來。在收到 Session cookie 後,應用程式將根據 Session ID 檢索 Session 資料,注意認證資訊已儲存在 Session 中,並將使用者視為「已認證」。
當遠端服務需要認證以存取 API 時,通常不會使用 cookies 進行認證,因為沒有 Web 瀏覽器。相反,遠端服務會在每個請求中向 API 發送 API token。應用程式可以根據有效 API tokens 的資料表驗證傳入的 token,並「認證」該請求是由與該 API token 關聯的使用者執行的。
Laravel 內建的瀏覽器認證服務 (Laravels Built In Browser Authentication Services)
Laravel 包含內建的認證和 Session 服務,通常透過 Auth 和 Session facades 存取。這些功能為從 Web 瀏覽器發起的請求提供基於 cookie 的認證。它們提供方法讓您驗證使用者的憑證並認證使用者。此外,這些服務會自動將適當的認證資料儲存在使用者的 Session 中並發送使用者的 Session cookie。本文件中包含如何使用這些服務的討論。
應用程式 Starter Kits
如本文件所討論的,您可以手動與這些認證服務互動,為您的應用程式建立自己的認證層。但是,為了幫助您更快地開始,我們發布了 免費的 starter kits,為整個認證層提供強大、現代的鷹架。
Laravel 的 API 認證服務 (Laravels Api Authentication Services)
Laravel 提供兩個可選套件來協助您管理 API tokens 和認證使用 API tokens 發出的請求:Passport 和 Sanctum。請注意,這些函式庫和 Laravel 內建的基於 cookie 的認證函式庫不是互斥的。這些函式庫主要專注於 API token 認證,而內建的認證服務專注於基於 cookie 的瀏覽器認證。許多應用程式將同時使用 Laravel 內建的基於 cookie 的認證服務和 Laravel 的 API 認證套件之一。
Passport
Passport 是一個 OAuth2 認證 provider,提供各種 OAuth2「授權類型」,允許您發送各種類型的 tokens。一般來說,這是一個用於 API 認證的強大且複雜的套件。但是,大多數應用程式不需要 OAuth2 規範提供的複雜功能,這對使用者和開發者來說可能會造成困惑。此外,開發者歷來對於如何使用像 Passport 這樣的 OAuth2 認證 providers 來認證 SPA 應用程式或行動應用程式感到困惑。
Sanctum
為了回應 OAuth2 的複雜性和開發者的困惑,我們著手建立一個更簡單、更精簡的認證套件,可以處理來自 Web 瀏覽器的第一方 Web 請求和透過 tokens 的 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 的 "它是如何運作的" 文件。
摘要與選擇您的技術堆疊 (Summary Choosing Your Stack)
總之,如果您的應用程式將使用瀏覽器存取,且您正在建立一個整體式 Laravel 應用程式,您的應用程式將使用 Laravel 內建的認證服務。
接下來,如果您的應用程式提供將由第三方使用的 API,您將在 Passport 或 Sanctum 之間選擇,為您的應用程式提供 API token 認證。一般來說,在可能的情況下應該優先使用 Sanctum,因為它是用於 API 認證、SPA 認證和行動認證的簡單、完整的解決方案,包括對「scopes」或「abilities」的支援。
如果您正在建立將由 Laravel 後端驅動的單頁應用程式 (SPA),您應該使用 Laravel Sanctum。當使用 Sanctum 時,您將需要 手動實作您自己的後端認證路由 或使用 Laravel Fortify 作為無頭認證後端服務,為註冊、密碼重設、電子郵件驗證等功能提供路由和 controllers。
當您的應用程式絕對需要 OAuth2 規範提供的所有功能時,可以選擇 Passport。
而且,如果您想快速開始,我們很高興推薦 我們的應用程式 starter kits 作為快速啟動新 Laravel 應用程式的方式,該應用程式已經使用了我們首選的 Laravel 內建認證服務和一個 Laravel API 認證套件的認證堆疊。
認證快速入門 (Authentication Quickstart)
[!WARNING] 本文件的這一部分討論透過 Laravel 應用程式 starter kits 認證使用者,其中包含 UI 鷹架來幫助您快速開始。如果您想直接與 Laravel 的認證系統整合,請查看有關 手動認證使用者 的文件。
安裝 Starter Kit (Install A Starter Kit)
首先,您應該 安裝 Laravel 應用程式 starter kit。我們的 starter kits 提供設計精美的起點,用於將認證納入您的全新 Laravel 應用程式。
取得已認證的使用者 (Retrieving The Authenticated User)
從 starter kit 建立應用程式並允許使用者註冊和認證您的應用程式後,您通常需要與目前已認證的使用者互動。在處理傳入請求時,您可以透過 Auth facade 的 user 方法存取已認證的使用者:
use Illuminate\Support\Facades\Auth;
// 取得目前已認證的使用者...
$user = Auth::user();
// 取得目前已認證使用者的 ID...
$id = Auth::id();
或者,一旦使用者已認證,您可以透過 Illuminate\Http\Request 實例存取已認證的使用者。請記住,型別提示的類別將自動注入到您的 controller 方法中。透過對 Illuminate\Http\Request 物件進行型別提示,您可以從應用程式中任何 controller 方法透過請求的 user 方法方便地存取已認證的使用者:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class FlightController extends Controller
{
/**
* 更新現有航班的航班資訊。
*/
public function update(Request $request): RedirectResponse
{
$user = $request->user();
// ...
return redirect('/flights');
}
}
判斷目前使用者是否已認證 (Determining If The Current User Is Authenticated)
若要判斷發出傳入 HTTP 請求的使用者是否已認證,您可以使用 Auth facade 上的 check 方法。如果使用者已認證,此方法將回傳 true:
use Illuminate\Support\Facades\Auth;
if (Auth::check()) {
// 使用者已登入...
}
[!NOTE] 儘管可以使用
check方法判斷使用者是否已認證,但您通常會使用 middleware 在允許使用者存取某些路由 / controllers 之前驗證使用者是否已認證。若要了解更多相關資訊,請查看有關 保護路由 的文件。
保護路由 (Protecting Routes)
路由 middleware 可用於僅允許已認證的使用者存取給定路由。Laravel 內建了 auth middleware,它是 Illuminate\Auth\Middleware\Authenticate 類別的 middleware 別名。由於此 middleware 已經在 Laravel 內部設定了別名,您只需將 middleware 附加到路由定義即可:
Route::get('/flights', function () {
// 只有已認證的使用者可以存取此路由...
})->middleware('auth');
重新導向未認證的使用者 (Redirecting Unauthenticated Users)
當 auth middleware 偵測到未認證的使用者時,它會將使用者重新導向到 login 命名路由。您可以使用應用程式 bootstrap/app.php 檔案中的 redirectGuestsTo 方法修改此行為:
use Illuminate\Http\Request;
->withMiddleware(function (Middleware $middleware): void {
$middleware->redirectGuestsTo('/login');
// 使用閉包...
$middleware->redirectGuestsTo(fn (Request $request) => route('login'));
})
重新導向已認證的使用者 (Redirecting Authenticated Users)
當 guest middleware 偵測到已認證的使用者時,它會將使用者重新導向到 dashboard 或 home 命名路由。您可以使用應用程式 bootstrap/app.php 檔案中的 redirectUsersTo 方法修改此行為:
use Illuminate\Http\Request;
->withMiddleware(function (Middleware $middleware): void {
$middleware->redirectUsersTo('/panel');
// 使用閉包...
$middleware->redirectUsersTo(fn (Request $request) => route('panel'));
})
指定 Guard (Specifying A Guard)
當將 auth middleware 附加到路由時,您還可以指定應使用哪個「guard」來認證使用者。指定的 guard 應對應於您的 auth.php 設定檔的 guards 陣列中的其中一個鍵:
Route::get('/flights', function () {
// 只有已認證的使用者可以存取此路由...
})->middleware('auth:admin');
登入節流 (Login Throttling)
如果您使用我們的 應用程式 starter kits 之一,速率限制將自動應用於登入嘗試。預設情況下,如果使用者在多次嘗試後未能提供正確的憑證,則使用者將無法登入一分鐘。節流對於使用者的使用者名稱 / 電子郵件地址及其 IP 位址是唯一的。
[!NOTE] 如果您想對應用程式中的其他路由進行速率限制,請查看 速率限制文件。
手動認證使用者 (Manually Authenticating Users)
您不需要使用 Laravel 應用程式 starter kits 中包含的認證鷹架。如果您選擇不使用此鷹架,您將需要直接使用 Laravel 認證類別來管理使用者認證。別擔心,這很容易!
我們將透過 Auth facade 存取 Laravel 的認證服務,因此我們需要確保在類別頂部匯入 Auth facade。接下來,讓我們看看 attempt 方法。attempt 方法通常用於處理來自應用程式「登入」表單的認證嘗試。如果認證成功,您應該重新產生使用者的 Session 以防止 session 固定攻擊:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/**
* 處理認證嘗試。
*/
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' => '提供的憑證與我們的記錄不符。',
])->onlyInput('email');
}
}
attempt 方法接受鍵 / 值對陣列作為其第一個引數。陣列中的值將用於在資料庫資料表中查找使用者。因此,在上面的範例中,使用者將透過 email 欄位的值被取得。如果找到使用者,將把儲存在資料庫中的雜湊密碼與透過陣列傳遞給方法的 password 值進行比較。您不應該對傳入請求的 password 值進行雜湊處理,因為框架會在與資料庫中的雜湊密碼進行比較之前自動對該值進行雜湊處理。如果兩個雜湊密碼匹配,將為使用者啟動已認證的 Session。
請記住,Laravel 的認證服務將根據您的認證 guard 的「provider」設定從資料庫中取得使用者。在預設的 config/auth.php 設定檔中,指定了 Eloquent 使用者 provider,並指示它在取得使用者時使用 App\Models\User model。您可以根據應用程式的需要在設定檔中更改這些值。
如果認證成功,attempt 方法將回傳 true。否則,將回傳 false。
Laravel 重新導向器提供的 intended 方法會將使用者重新導向到他們在被認證 middleware 攔截之前嘗試存取的 URL。如果預期目的地不可用,可以給此方法提供一個備用 URI。
指定額外條件 (Specifying Additional Conditions)
如果您願意,除了使用者的電子郵件和密碼之外,您還可以向認證查詢新增額外的查詢條件。為此,我們只需將查詢條件新增到傳遞給 attempt 方法的陣列中。例如,我們可以驗證使用者是否被標記為「active」:
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// 認證成功...
}
對於複雜的查詢條件,您可以在憑證陣列中提供閉包。此閉包將與查詢實例一起調用,允許您根據應用程式的需求自訂查詢:
use Illuminate\Database\Eloquent\Builder;
if (Auth::attempt([
'email' => $email,
'password' => $password,
fn (Builder $query) => $query->has('activeSubscription'),
])) {
// 認證成功...
}
[!WARNING] 在這些範例中,
attemptWhen 方法接收閉包作為其第二個引數,可用於在實際認證使用者之前對潛在使用者執行更廣泛的檢查。閉包接收潛在使用者,並應回傳 true 或 false 以指示是否可以認證該使用者:
if (Auth::attemptWhen([
'email' => $email,
'password' => $password,
], function (User $user) {
return $user->isNotBanned();
})) {
// 認證成功...
}
存取特定 Guard 實例 (Accessing Specific Guard Instances)
透過 Auth facade 的 guard 方法,您可以指定在認證使用者時要使用哪個 guard 實例。這允許您使用完全獨立的可認證 models 或使用者資料表來管理應用程式不同部分的認證。
傳遞給 guard 方法的 guard 名稱應對應於您的 auth.php 設定檔中設定的其中一個 guards:
if (Auth::guard('admin')->attempt($credentials)) {
// ...
}
記住使用者 (Remembering Users)
許多 Web 應用程式在其登入表單上提供「記住我」核取方塊。如果您想在應用程式中提供「記住我」功能,您可以將布林值作為第二個引數傳遞給 attempt 方法。
當此值為 true 時,Laravel 將無限期地保持使用者已認證狀態,或直到他們手動登出。您的 users 資料表必須包含字串 remember_token 欄位,該欄位將用於儲存「記住我」token。新 Laravel 應用程式中包含的 users 資料表 migration 已經包含此欄位:
use Illuminate\Support\Facades\Auth;
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// 正在記住使用者...
}
如果您的應用程式提供「記住我」功能,您可以使用 viaRemember 方法來判斷目前已認證的使用者是否使用「記住我」cookie 進行認證:
use Illuminate\Support\Facades\Auth;
if (Auth::viaRemember()) {
// ...
}
其他認證方法 (Other Authentication Methods)
認證使用者實例 (Authenticate A User Instance)
如果您需要將現有的使用者實例設定為目前已認證的使用者,您可以將使用者實例傳遞給 Auth facade 的 login 方法。給定的使用者實例必須是 Illuminate\Contracts\Auth\Authenticatable contract 的實作。Laravel 內含的 App\Models\User model 已經實作了此介面。當您已經有一個有效的使用者實例時(例如在使用者註冊到您的應用程式後),這種認證方法非常有用:
use Illuminate\Support\Facades\Auth;
Auth::login($user);
您可以將布林值作為第二個引數傳遞給 login 方法。此值指示是否需要為已認證的 Session 提供「記住我」功能。請記住,這意味著 Session 將無限期地被認證,或直到使用者手動登出應用程式:
Auth::login($user, $remember = true);
如果需要,您可以在呼叫 login 方法之前指定認證 guard:
Auth::guard('admin')->login($user);
透過 ID 認證使用者 (Authenticate A User By Id)
若要使用使用者資料庫記錄的主鍵來認證使用者,您可以使用 loginUsingId 方法。此方法接受您要認證的使用者的主鍵:
Auth::loginUsingId(1);
您可以將布林值傳遞給 loginUsingId 方法的 remember 引數。此值指示是否需要為已認證的 Session 提供「記住我」功能。請記住,這意味著 Session 將無限期地被認證,或直到使用者手動登出應用程式:
Auth::loginUsingId(1, remember: true);
單次認證使用者 (Authenticate A User Once)
您可以使用 once 方法為單一請求認證使用者到應用程式。呼叫此方法時不會使用 Sessions 或 cookies,並且不會觸發 Login 事件:
if (Auth::once($credentials)) {
// ...
}
HTTP Basic 認證 (HTTP Basic Authentication)
HTTP Basic 認證 提供了一種快速方式來認證應用程式的使用者,而無需設定專用的「登入」頁面。若要開始,請將 auth.basic middleware 附加到路由。auth.basic middleware 已包含在 Laravel 框架中,因此您不需要定義它:
Route::get('/profile', function () {
// 只有已認證的使用者可以存取此路由...
})->middleware('auth.basic');
一旦 middleware 附加到路由,當您在瀏覽器中存取該路由時,將自動提示您輸入憑證。預設情況下,auth.basic middleware 將假設 users 資料庫資料表中的 email 欄位是使用者的「使用者名稱」。
FastCGI 注意事項 (A Note On Fastcgi)
如果您使用 PHP FastCGI 和 Apache 來提供您的 Laravel 應用程式服務,HTTP Basic 認證可能無法正常運作。若要修正這些問題,可以將以下行新增到您的應用程式的 .htaccess 檔案中:
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
無狀態 HTTP Basic 認證 (Stateless Http Basic Authentication)
您也可以使用 HTTP Basic 認證而不在 Session 中設定使用者識別符 cookie。如果您選擇使用 HTTP 認證來認證對應用程式 API 的請求,這主要很有用。若要完成此操作,請 定義一個 middleware 來呼叫 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
{
/**
* 處理傳入的請求。
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
return Auth::onceBasic() ?: $next($request);
}
}
接下來,將 middleware 附加到路由:
Route::get('/api/user', function () {
// 只有已認證的使用者可以存取此路由...
})->middleware(AuthenticateOnceWithBasicAuth::class);
登出 (Logging Out)
若要手動將使用者登出應用程式,您可以使用 Auth facade 提供的 logout 方法。這將從使用者的 Session 中移除認證資訊,以便後續請求不會被認證。
除了呼叫 logout 方法外,建議您使使用者的 Session 失效並重新產生其 CSRF token。登出使用者後,您通常會將使用者重新導向到應用程式的根目錄:
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
/**
* 將使用者登出應用程式。
*/
public function logout(Request $request): RedirectResponse
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
使其他裝置上的 Session 失效 (Invalidating Sessions On Other Devices)
Laravel 還提供了一種機制,用於使使用者在其他裝置上活躍的 Sessions 失效並「登出」,而不會使其目前裝置上的 Session 失效。此功能通常在使用者更改或更新密碼時使用,您希望使其他裝置上的 Sessions 失效,同時保持目前裝置已認證。
開始之前,您應該確保應接收 Session 認證的路由已包含 Illuminate\Session\Middleware\AuthenticateSession middleware。通常,您應該將此 middleware 放在路由群組定義中,以便它可以套用於您應用程式的大多數路由。預設情況下,AuthenticateSession middleware 可以使用 auth.session middleware 別名 附加到路由:
Route::middleware(['auth', 'auth.session'])->group(function () {
Route::get('/', function () {
// ...
});
});
然後,您可以使用 Auth facade 提供的 logoutOtherDevices 方法。此方法要求使用者確認其目前密碼,您的應用程式應透過輸入表單接受該密碼:
use Illuminate\Support\Facades\Auth;
Auth::logoutOtherDevices($currentPassword);
當調用 logoutOtherDevices 方法時,使用者的其他 Sessions 將完全失效,這意味著他們將從之前認證的所有 guards「登出」。
密碼確認 (Password Confirmation)
在建立應用程式時,您可能偶爾會有一些動作應該要求使用者在執行動作之前或在使用者被重新導向到應用程式的敏感區域之前確認其密碼。Laravel 包含內建的 middleware 使此過程變得輕而易舉。實作此功能將要求您定義兩個路由:一個路由用於顯示要求使用者確認密碼的視圖,另一個路由用於確認密碼有效並將使用者重新導向到其預期目的地。
[!NOTE] 以下文件討論如何直接與 Laravel 的密碼確認功能整合;但是,如果您想更快地開始,Laravel 應用程式 starter kits 包含對此功能的支援!
設定 (Configuration)
確認密碼後,使用者在三小時內不會被要求再次確認密碼。但是,您可以透過更改應用程式 config/auth.php 設定檔中的 password_timeout 設定值來設定使用者重新提示輸入密碼之前的時間長度。
路由 (Routing)
密碼確認表單 (The Password Confirmation Form)
首先,我們將定義一個路由來顯示要求使用者確認密碼的視圖:
Route::get('/confirm-password', function () {
return view('auth.confirm-password');
})->middleware('auth')->name('password.confirm');
如您所料,此路由回傳的視圖應該有一個包含 password 欄位的表單。此外,請隨意在視圖中包含說明使用者正在進入應用程式的受保護區域並必須確認密碼的文字。
確認密碼 (Confirming The Password)
接下來,我們將定義一個路由來處理來自「確認密碼」視圖的表單請求。此路由將負責驗證密碼並將使用者重新導向到其預期目的地:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
Route::post('/confirm-password', function (Request $request) {
if (! Hash::check($request->password, $request->user()->password)) {
return back()->withErrors([
'password' => ['提供的密碼與我們的記錄不符。']
]);
}
$request->session()->passwordConfirmed();
return redirect()->intended();
})->middleware(['auth', 'throttle:6,1']);
在繼續之前,讓我們更詳細地檢查這個路由。首先,判斷請求的 password 欄位是否確實與已認證使用者的密碼相符。如果密碼有效,我們需要通知 Laravel 的 Session 使用者已確認其密碼。passwordConfirmed 方法將在使用者的 Session 中設定一個時間戳,Laravel 可以使用該時間戳來判斷使用者上次確認密碼的時間。最後,我們可以將使用者重新導向到其預期目的地。
保護路由 (Protecting Routes)
您應該確保任何執行需要近期密碼確認動作的路由都已指派 password.confirm middleware。此 middleware 包含在 Laravel 的預設安裝中,並會自動將使用者的預期目的地儲存在 Session 中,以便使用者在確認密碼後可以被重新導向到該位置。在 Session 中儲存使用者的預期目的地後,middleware 將把使用者重新導向到 password.confirm 命名路由:
Route::get('/settings', function () {
// ...
})->middleware(['password.confirm']);
Route::post('/settings', function () {
// ...
})->middleware(['password.confirm']);
新增自訂 Guards (Adding Custom Guards)
您可以使用 Auth facade 上的 extend 方法定義自己的認證 guards。您應該在 service provider 中呼叫 extend 方法。由於 Laravel 已經內建了 AppServiceProvider,我們可以將程式碼放在該 provider 中:
<?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
{
// ...
/**
* 啟動任何應用程式服務。
*/
public function boot(): void
{
Auth::extend('jwt', function (Application $app, string $name, array $config) {
// 回傳 Illuminate\Contracts\Auth\Guard 的實例...
return new JwtGuard(Auth::createUserProvider($config['provider']));
});
}
}
如您在上面的範例中所見,傳遞給 extend 方法的回呼應該回傳 Illuminate\Contracts\Auth\Guard 的實作。此介面包含您需要實作的幾個方法來定義自訂 guard。一旦您的自訂 guard 已定義,您可以在 auth.php 設定檔的 guards 設定中參考該 guard:
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
Closure Request Guards
實作自訂、基於 HTTP 請求的認證系統的最簡單方式是使用 Auth::viaRequest 方法。此方法允許您使用單一閉包快速定義認證過程。
若要開始,請在應用程式的 AppServiceProvider 的 boot 方法中呼叫 Auth::viaRequest 方法。viaRequest 方法接受認證驅動程式名稱作為其第一個引數。此名稱可以是描述您自訂 guard 的任何字串。傳遞給方法的第二個引數應該是一個閉包,它接收傳入的 HTTP 請求並回傳使用者實例,或者如果認證失敗則回傳 null:
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
/**
* 啟動任何應用程式服務。
*/
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',
],
],
最後,當將認證 middleware 指派給路由時,您可以參考該 guard:
Route::middleware('auth:api')->group(function () {
// ...
});
新增自訂使用者 Providers (Adding Custom User Providers)
如果您不使用傳統的關聯式資料庫來儲存使用者,您將需要使用自己的認證使用者 provider 來擴充 Laravel。我們將使用 Auth facade 上的 provider 方法來定義自訂使用者 provider。使用者 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
{
// ...
/**
* 啟動任何應用程式服務。
*/
public function boot(): void
{
Auth::provider('mongo', function (Application $app, array $config) {
// 回傳 Illuminate\Contracts\Auth\UserProvider 的實例...
return new MongoUserProvider($app->make('mongo.connection'));
});
}
}
使用 provider 方法註冊 provider 後,您可以在 auth.php 設定檔中切換到新的使用者 provider。首先,定義一個使用您新驅動程式的 provider:
'providers' => [
'users' => [
'driver' => 'mongo',
],
],
最後,您可以在 guards 設定中參考此 provider:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],
User Provider Contract
Illuminate\Contracts\Auth\UserProvider 實作負責從持久性儲存系統(如 MySQL、MongoDB 等)中獲取 Illuminate\Contracts\Auth\Authenticatable 實作。這兩個介面允許 Laravel 認證機制繼續運作,無論使用者資料如何儲存或使用什麼類型的類別來表示已認證的使用者:
讓我們看看 Illuminate\Contracts\Auth\UserProvider contract:
<?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 這樣的資料庫欄位中)來取得使用者。與前一個方法一樣,此方法應該回傳具有匹配 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'] 的值。此方法應該回傳 true 或 false 以指示密碼是否有效。
rehashPasswordIfRequired 方法應該在需要且支援時重新雜湊給定 $user 的密碼。例如,此方法通常會使用 Hash::needsRehash 方法來判斷 $credentials['password'] 值是否需要重新雜湊。如果密碼需要重新雜湊,該方法應該使用 Hash::make 方法來重新雜湊密碼並更新底層持久性儲存中的使用者記錄。
Authenticatable Contract
現在我們已經探索了 UserProvider 上的每個方法,讓我們看看 Authenticatable contract。請記住,使用者 providers 應該從 retrieveById、retrieveByToken 和 retrieveByCredentials 方法回傳此介面的實作:
<?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 類別。
自動密碼重新雜湊 (Automatic Password Rehashing)
Laravel 的預設密碼雜湊演算法是 bcrypt。bcrypt 雜湊的「工作因子」可以透過應用程式的 config/hashing.php 設定檔或 BCRYPT_ROUNDS 環境變數進行調整。
通常,隨著 CPU / GPU 處理能力的增加,bcrypt 工作因子應該隨著時間增加。如果您為應用程式增加 bcrypt 工作因子,Laravel 將在使用者透過 Laravel 的 starter kits 認證您的應用程式時,或當您透過 attempt 方法 手動認證使用者 時,優雅且自動地重新雜湊使用者密碼。
通常,自動密碼重新雜湊不應該中斷您的應用程式;但是,您可以透過發布 hashing 設定檔來停用此行為:
php artisan config:publish hashing
一旦設定檔已發布,您可以將 rehash_on_login 設定值設定為 false:
'rehash_on_login' => false,
事件 (Events)
Laravel 在認證過程中會觸發各種 事件。您可以為以下任何事件 定義 listeners:
| 事件名稱 |
|---|
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 |
Illuminate\Auth\Events\PasswordResetLinkSent |