駭客也搖頭!Laravel API 終極驗證與中介層客製化防禦聖經
你的 Laravel API 仍在「裸奔」嗎?資深工程師 Eric 警告,資料驗證 (Validation) 與請求過濾 (Middleware) 是 API 的兩道生命線。本文將教你如何超越基礎 required 規則,客製化如台灣身分證字號的驗證邏輯,並打造帶參數的彈性 Middleware 進行精準權限控管。這不僅是優化程式碼,更是建立讓駭客難以攻破的銅牆鐵壁。立即掌握這些專業技巧,擺脫「義大利麵」程式碼,將你的專案提升到企業級安全層次!
你的 Laravel API 正在裸奔嗎?終極驗證 (Validation) 與中介層 (Middleware) 客製化聖經,打造駭客也搖頭的銅牆鐵壁
嗨,我是浪花科技的資深工程師 Eric。寫了這麼多年的程式,我看過太多專案因為一個小小的疏忽而門戶大開,尤其是在 API 開發的世界裡。很多工程師覺得 API 只要能動、能回傳 JSON 就好,卻忽略了最重要的兩道防線:資料的「驗證 (Validation)」與請求的「過濾 (Middleware)」。
這就像蓋了一棟豪宅,卻裝了個紙糊的大門,還忘了請保全。任何阿貓阿狗都能隨便進出,甚至在你家裡塗鴉、搬走你的家具。今天,我就要以一個老工程師的囉嗦精神,帶你深入探討 Laravel 驗證與 Middleware 客製化 的藝術,不只是教你怎麼用,更是要教你如何打造一個讓駭客看到都想繞道的堅固防線。
第一道防線:Laravel 驗證 (Validation) – 不只是 `required` 這麼簡單
「Garbage in, garbage out.」這句古老的工程師諺語,點出了驗證的核心價值。如果你的應用程式從一開始就接收了格式錯誤、不合邏輯的垃圾資料,那後續的商業邏輯再怎麼天花亂墜,產出的結果也只會是一場災難。Laravel 內建的驗證器非常強大,但真正的戰場往往在那些內建規則無法觸及的灰色地帶。
為何 Validation 是你的第一道防線?
想像一下,使用者註冊時,你要求輸入手機號碼,但沒有驗證格式,結果資料庫裡存了一筆「我的手機號碼是這個」。當你需要發送簡訊驗證碼時,系統就直接崩潰了。Validation 的目的,就是在這些髒資料接觸到你核心的商業邏輯或寫入資料庫之前,就把它們優雅地擋在門外,並給予使用者明確的提示。
超越基礎:打造你的客製化驗證規則 (Custom Validation Rules)
當內建的 `email`, `numeric`, `max:255` 不夠用時,就是我們捲起袖子自己動手的時候了。例如,你需要驗證一個台灣的身分證字號,這顯然沒有內建規則可以用。這時候,`php artisan make:rule` 就是你的好朋友。
假設我們要建立一個 `TaiwanIdNumber` 的驗證規則:
php artisan make:rule TaiwanIdNumber
接著 Laravel 會在 `app/Rules` 資料夾下產生一個 `TaiwanIdNumber.php` 檔案。我們需要實作 `passes` 方法(驗證邏輯)和 `message` 方法(錯誤訊息):
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class TaiwanIdNumber implements Rule
{
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$value = strtoupper($value);
if (!preg_match('/^[A-Z][12]\d{8}$/', $value)) {
return false;
}
// 這是一個簡化的檢查碼驗證邏輯,僅供示範
$idHeader = 'ABCDEFGHJKLMNPQRSTUVWXYZIO';
$idHeaderMap = str_split($idHeader);
$map = array_flip($idHeaderMap);
$n1 = floor(($map[$value[0]] + 10) / 10);
$n2 = ($map[$value[0]] + 10) % 10;
$nums = str_split(substr($value, 1));
$sum = $n1 + ($n2 * 9);
for ($i = 0; $i < 8; $i++) {
$sum += $nums[$i] * (8 - $i);
}
$sum += $nums[8];
return ($sum % 10 === 0);
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return '您輸入的身分證字號格式不正確。';
}
}
有了這個規則後,在你的 Controller 或是 Form Request 中就可以像使用內建規則一樣呼叫它:
use App\Rules\TaiwanIdNumber;
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'tw_id' => ['required', 'string', new TaiwanIdNumber],
]);
我個人強烈建議,只要驗證邏輯稍微複雜,就應該使用 Form Request 來組織你的驗證邏輯,而不是全部塞在 Controller 裡,這對於大型專案的維護性來說,簡直是天壤之別。
第二道防線:Laravel Middleware – 你應用程式的忠實保鑣
如果說 Validation 是檢查訪客「攜帶的物品」(資料)是否合規,那 Middleware 就是檢查訪客「本身」的身份。他是個盡責的保鑣,在請求(Request)真正抵達你的 Controller 之前,對其進行層層過濾和檢查。
Middleware 到底是什麼?
你可以把 Middleware 想像成洋蔥的一層層外皮。一個 HTTP 請求進來,必須先通過全球 Middleware,再通過路由群組的 Middleware,最後通過單一路由的 Middleware,才能見到 Controller 本人。任何一層檢查不通過,請求就會被直接擋下並回傳錯誤,連 Controller 的門都摸不到。
常見的應用場景包括:
- 權限驗證:使用者是否登入?是否為管理員?
- API 金鑰驗證:請求是否攜帶了合法的 API Key?
- 日誌記錄:記錄所有進入特定路由的請求。
- 維護模式:在網站維護時,顯示特定的維護頁面。
打造你的客製化 Middleware
讓我們來實作一個常見的需求:檢查 API 請求的 Header 是否帶有有效的 `X-API-KEY`。首先,建立 Middleware:
php artisan make:middleware EnsureApiKeyIsValid
這會在 `app/Http/Middleware` 中建立 `EnsureApiKeyIsValid.php`。我們來修改 `handle` 方法:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class EnsureApiKeyIsValid
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
// 從 .env 檔案中取得我們設定的有效 API Key
$validApiKey = config('services.api.key');
if (!$request->hasHeader('X-API-KEY') || $request->header('X-API-KEY') !== $validApiKey) {
// 如果 Header 不存在或 Key 不正確,回傳 401 Unauthorized
return response()->json(['message' => 'Unauthorized.'], 401);
}
// 驗證通過,將請求交給下一個 Middleware 或 Controller
return $next($request);
}
}
接著,我們必須註冊這個 Middleware。打開 `app/Http/Kernel.php`,在 `$routeMiddleware` 陣列中加入:
protected $routeMiddleware = [
// ... 其他 middleware
'api.key' => \App\Http\Middleware\EnsureApiKeyIsValid::class,
];
現在,你可以在你的 `routes/api.php` 檔案中,保護需要驗證金鑰的路由了:
Route::get('/protected-data', [DataController::class, 'index'])->middleware('api.key');
// 或是保護一整個路由群組
Route::middleware(['api.key'])->group(function () {
Route::get('/profile', [ProfileController::class, 'show']);
Route::post('/settings', [SettingsController::class, 'update']);
});
這種方式遠比在每個 Controller 方法中都寫一次 API Key 驗證邏輯要來得優雅且容易維護。如果你需要更複雜的認證機制,例如 JWT,可以參考我們關於 Laravel JWT 深度實戰的文章,原理是相通的。
帶參數的 Middleware:打造更靈活的權限控管
有時候,我們希望 Middleware 能更靈活一點。例如,一個檢查使用者角色的 Middleware,我們希望可以動態傳入需要的角色名稱,而不是寫死一個 `IsAdmin`、一個 `IsEditor`…。
讓我們建立一個 `CheckRole` Middleware:
php artisan make:middleware CheckRole
修改 `handle` 方法,讓它可以接收額外的參數:
public function handle(Request $request, Closure $next, string $role)
{
if (! $request->user() || ! $request->user()->hasRole($role)) {
// 這裡的 hasRole 是一個假設你在 User Model 中實作的方法
abort(403, 'Access Denied.');
}
return $next($request);
}
註冊後,你就可以這樣使用它:
Route::post('/posts', [PostController::class, 'store'])->middleware('role:editor');
Route::delete('/posts/{post}', [PostController::class, 'destroy'])->middleware('role:admin');
看到沒?一個 Middleware 就能處理多種角色的驗證,這就是客製化的威力!
總結:防線是疊加上去的,不是單點突破
寫到這裡,你應該能體會到,一個安全的 Laravel 應用程式,它的防線是立體的。Middleware 像是城牆與衛兵,負責阻擋不合法的身份;Validation 則是進城後的安檢,確保每個人攜帶的物品都安全無虞。兩者各司其職,缺一不可。
千萬不要再寫那種把所有邏輯都塞在 Controller 裡的「義大利麵」程式碼了。善用 Laravel 驗證與 Middleware 客製化 的強大功能,不僅能讓你的程式碼更清晰、更易於維護,更能為你的應用程式建立起一道道堅不可摧的防線。這是一個專業工程師的基本素養,也是對使用者資料負責的表現。
延伸閱讀
- 你的 Laravel 專案是技術債炸彈還是傳世藝術品?Laravel 10 專案架構最佳實務指南
- API 沒上鎖,等於家裡沒關門!Laravel JWT 終極實戰,手把手打造無狀態認證金鑰
- 不只是 CRUD!Laravel Eloquent ORM 終極實戰聖經:從模型、關聯到效能優化的屠龍之術
如果你對於如何將這些高階的開發技巧應用到你的 WordPress 網站,或是想打造更複雜的企業級系統感到好奇,甚至是遇到了難以解決的技術瓶頸,都歡迎與浪花科技的團隊聊聊。我們樂於分享我們的經驗,幫助你的專案提升到新的層次。
常見問題 (FAQ)
Q1: Laravel 的驗證 (Validation) 和中介層 (Middleware) 主要區別是什麼?
簡單來說,Validation 負責檢查「資料」的正確性與完整性,例如電子郵件格式是否正確、密碼長度是否足夠。而 Middleware 則負責過濾「請求」本身,處理的是身份驗證、權限控管等問題,它在請求到達你的主要邏輯之前就進行檢查。一個是安檢,一個是查驗身份。
Q2: 什麼時候該用客製化驗證規則 (Custom Rule),而不是在 Controller 用閉包 (Closure) 寫驗證?
當一個驗證邏輯很複雜,或者需要在多個地方重複使用時,就應該建立一個客製化的 Rule class。這遵循了 DRY (Don’t Repeat Yourself) 原則,讓你的程式碼更乾淨、可讀性更高,也更容易進行單元測試。如果只是一個非常簡單且只用一次的驗證,用閉包是可行的,但長遠來看,建立 Rule class 是更專業的做法。
Q3: 一個路由 (Route) 可以套用多個 Middleware 嗎?
絕對可以!你可以將多個 Middleware 的別名放在一個陣列中傳給 `middleware()` 方法,例如 `->middleware([‘auth’, ‘role:admin’])`。請求會依照陣列中的順序依序通過這些 Middleware,只要其中一個不通過,請求就會被中斷。順序非常重要,例如你必須先驗證登入 (`auth`),才能接著驗證角色 (`role`)。
Q4: 驗證邏輯的最佳實踐是放在哪裡?Controller 還是 Form Request?
對於非常簡單的驗證(一兩個欄位),放在 Controller 中使用 `$request->validate()` 是可以接受的。但只要驗證規則超過三四條,或是有複雜的條件式驗證,強烈建議使用 Form Request。它可以將驗證邏輯從 Controller 中抽離,讓 Controller 專注於處理商業邏輯,使程式碼結構更清晰,也更符合「單一職責原則」。






