你的 API 登入機制安全嗎?Laravel 11 JWT 進階實戰:Refresh Token 輪替與資安防護全攻略

2026/01/11 | API 串接與自動化, Laravel技術分享, 全端與程式開發, 網站安全與防護

終結一年期 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

  1. 發送請求。
  2. 收到 401 錯誤。
  3. 檢查錯誤碼是否為 Token Expired
  4. 若是,暫停所有請求,發送 /api/refresh 請求。
  5. 拿到新 Token,更新 LocalStorage/Cookie。
  6. 重試原本失敗的請求。

資安加固: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 與 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。

 
立即諮詢,索取免費1年網站保固