終結一年期 Access Token!Laravel 11 JWT 輪替與資安實戰
你的 API 登入機制還停留在「會動就好」的階段嗎?資深工程師 Eric 警告,將 Access Token 設為一年形同門戶大開!本文將帶你深入 Laravel 11 的新架構,實戰雙重 Token 機制(短命 AT + 長命 RT)。我們將解密 Refresh Token 輪替的技術細節,包含如何處理 Laravel 11 的 Middleware 變革,以及如何設定關鍵的「黑名單寬限期」來防止高並發請求衝突。別再讓資安成為專案的隱憂,立即學習如何使用 HttpOnly Cookie 等方法加固你的無狀態認證系統,打造既安全又流暢的高可用 API 服務!現在就聯繫浪花科技,升級您的資安架構!
你的 API 登入機制安全嗎?Laravel 11 JWT 進階實戰:Refresh Token 輪替與資安防護全攻略
大家好,我是浪花科技的資深工程師 Eric。最近在幫客戶做 Code Review 的時候,看到一個很有趣(或者說驚悚)的現象。很多剛接觸 Laravel API 開發的朋友,對於 JWT(JSON Web Token)的理解還停留在「安裝套件、拿到 Token、放在 Header 裡發送」就結束了。
甚至我看過有專案為了「方便」,直接把 Access Token 的過期時間設成 一年。這種做法跟把家裡鑰匙放在大門口地墊下一樣,只要鑰匙(Token)被偷,駭客就有整整一年的時間在你家進出自如。這不是 API 開發,這是在開玩笑。
隨著 Laravel 11 的發布,專案結構有了不小的變動(尤其是 Middleware 的註冊方式),今天我們就來談談在 2025 年的現在,如何正確、安全地實作 Laravel API 開發與 JWT 認證,重點會放在「Refresh Token 輪替機制」與 Laravel 11 的整合實戰。
為什麼 Access Token 不能設永久?
在深入程式碼之前,我們先來聊聊架構。JWT 的最大優勢是無狀態(Stateless),伺服器不需要查資料庫就能驗證使用者身份。但這也是它的雙面刃:一旦 Token 發出去,在過期之前,伺服器很難「收回」它(除非你用了黑名單機制,但這又變回有狀態了)。
為了平衡安全與體驗,標準的 JWT 認證架構應該包含兩種 Token:
- Access Token(存取權杖): 壽命短(例如 15-60 分鐘)。用來請求 API 資源。就算被偷,駭客也只能爽一下下。
- Refresh Token(刷新權杖): 壽命長(例如 2 週)。用來在 Access Token 過期後,換取新的 Access Token。通常儲存在更安全的地方(如 HttpOnly Cookie),並且只能使用一次(Refresh Rotation)。
Laravel 11 環境準備與套件選擇
在 Laravel 社群中,原本主流的 tymon/jwt-auth 更新頻率較低,目前業界多推薦使用其分支 php-open-source-saver/jwt-auth,它對新版 PHP 和 Laravel 的支援度更好。
1. 安裝套件
composer require php-open-source-saver/jwt-auth
2. 發布設定檔
php artisan vendor:publish --provider="PHPOpenSourceSaver\JWTAuth\Providers\LaravelServiceProvider"
3. 產生 JWT Secret
php artisan jwt:secret
這一步很重要,它會更新你的 .env 檔。記得,這個 Secret 千萬不能進版控(Git),否則你的簽章就毫無安全性可言。
Laravel 11 的 Middleware 變革
這是 Eric 覺得 Laravel 11 改動最有感的地方。以前我們習慣去 app/Http/Kernel.php 註冊 Middleware,但現在 Kernel 檔案已經被移除了!所有的 Middleware 設定都移到了 bootstrap/app.php。
如果你的 JWT 套件需要註冊 Alias 或全域 Middleware,要在這裡設定:
// bootstrap/app.php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
// Laravel 11 註冊別名的方式
$middleware->alias([
'auth.jwt' => \PHPOpenSourceSaver\JWTAuth\Http\Middleware\Authenticate::class,
]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
實作 Refresh Token 輪替機制 (Token Rotation)
這是本篇文章的精華。我們不只要讓使用者能登入,還要讓他們能「無感續期」。當前端發現 API 回傳 401 Unauthorized (Token Expired) 時,應該自動拿 Refresh Token 去換新的 Access Token。
AuthController 的設計
這是一個標準的 Laravel Controller 範例,我們重點看 refresh 方法:
// app/Http/Controllers/AuthController.php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
class AuthController extends Controller
{
public function __construct()
{
// 除了 login,其他都需要驗證
// 注意:refresh 路由通常也會需要驗證舊的 token 格式,或者允許過期的 token 通過 middleware
}
/**
* 刷新 Token
*/
public function refresh()
{
try {
// 這裡會將舊的 Token 加入黑名單,並回傳新的 Token
$newToken = Auth::refresh();
return $this->respondWithToken($newToken);
} catch (\Exception $e) {
// 如果 Refresh Token 也過期了,或是已被加入黑名單
return response()->json(['error' => 'Refresh token expired or invalid'], 401);
}
}
/**
* 統一的回傳格式
*/
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => Auth::factory()->getTTL() * 60 // 單位是秒
]);
}
}
設定 config/jwt.php 的關鍵參數
為了安全,我們必須啟用黑名單機制,確保舊的 Token 在刷新後立即失效。打開 config/jwt.php 檢查以下設定:
'blacklist_enabled' => true,:一定要開。'ttl' => 60,:Access Token 存活時間,建議短一點(分鐘)。'refresh_ttl' => 20160,:Refresh Token 存活時間(分鐘),通常設為兩週。'blacklist_grace_period' => 30,:這個超重要!
為什麼要有 Grace Period (寬限期)?這是我在處理高並發 API 時血淋淋的教訓。當前端同時發出 5 個 API 請求,剛好 Token 過期了。第一個請求觸發了 Refresh,舊 Token 被作廢。這時候第 2 到第 5 個請求如果帶著舊 Token 到達伺服器,會因為「Token 已在黑名單」而被拒絕,導致前端炸裂。
設定 30 秒的寬限期,允許舊 Token 在刷新後的 30 秒內依然可以通過驗證(或是被視為有效),能完美解決並發請求的問題。
前端的處理邏輯(給全端工程師的建議)
雖然我們是寫後端,但了解前端如何接球很重要。前端應該使用 Axios Interceptor:
- 發送請求。
- 收到
401錯誤。 - 檢查錯誤碼是否為
Token Expired。 - 若是,暫停所有請求,發送
/api/refresh請求。 - 拿到新 Token,更新 LocalStorage/Cookie。
- 重試原本失敗的請求。
資安加固:HttpOnly Cookie
將 Token 存在 LocalStorage 是最簡單的,但也最容易受到 XSS(跨站腳本攻擊)的威脅。如果你的專案對安全性要求極高,Eric 建議將 Token 寫入 HttpOnly Cookie。
在 Laravel 中,你可以建立一個 Middleware 將 Response 加上 Cookie:
$cookie = cookie('jwt_token', $token, 60, null, null, true, true); // HttpOnly = true
return response()->json([...])->withCookie($cookie);
這樣 JavaScript 就讀不到 Token,大大降低了 Token 被竊取的風險。
結論
Laravel API 開發與 JWT 認證絕對不是「會動就好」。在 Laravel 11 的新架構下,我們更要注意 Middleware 的配置位置,以及 Token 的生命週期管理。透過合理的 TTL 設定、黑名單寬限期,以及正確的前端攔截機制,我們才能打造出一個既安全又順暢的 API 系統。
別再當那個把 Access Token 設為一年的工程師了,好嗎?
延伸閱讀
- 你的 API 像公共廁所隨便進?Laravel 11 驗證 (Validation) 與 Middleware 客製化終極實戰
- API 沒上鎖,等於家裡沒關門!Laravel JWT 終極實戰,手把手打造無狀態認證金鑰
- API 半夜又斷線?資深工程師的 WordPress API 串接防爆聖經:從 Rate Limit 到優雅重試
如果你的企業系統正面臨 API 效能瓶頸,或是對於資安架構感到不放心,歡迎隨時聯繫浪花科技。我們專注於打造高流量、高可用的 Laravel 與 WordPress 解決方案。
常見問題 (FAQ)
Q1: Laravel 11 移除了 Kernel.php,我該去哪裡註冊 JWT Middleware?
在 Laravel 11 中,所有的 Middleware 設定都移到了 bootstrap/app.php。你可以使用 ->withMiddleware() 方法並呼叫 $middleware->alias() 來註冊你的 JWT Middleware 別名。
Q2: 為什麼我的 Refresh Token 一刷新,其他的並行請求就全部失敗了?
這通常是因為這幾個請求帶著舊的 Token 同時抵達伺服器,第一個請求刷新後,舊 Token 立即進入黑名單,導致後續請求被拒。解決方法是在 config/jwt.php 中設定 blacklist_grace_period(例如 30 秒),給予舊 Token 一點緩衝時間。
Q3: JWT Token 應該存在 LocalStorage 還是 Cookie?
LocalStorage 實作簡單但易受 XSS 攻擊;HttpOnly Cookie 安全性較高但需處理 CSRF 問題。如果是高安全性要求的金融或個資應用,強烈建議使用 HttpOnly Cookie;一般應用則可視風險評估選擇 LocalStorage。






