2018年11月12日 星期一

《面試官別再問》PHP Laravel 如何使用JWT api authentication

 使用 JWT 實作 Laravel API 驗證與刷新機制

在現代 Web 開發中,API 驗證扮演著核心角色。當我們需要一個無狀態、可擴展且能與多種前端(如 SPA、行動應用)協作的驗證方案時,JWT(JSON Web Tokens)是個極佳的選擇。本文將深入探討如何在 Laravel 專案中,透過第三方套件實作一套完整且安全的 JWT API 驗證機制,並詳細說明如何處理 Token 刷新,以兼顧便利性與安全性。


為什麼選擇 JWT 進行 API 驗證?

JWT 的核心優勢在於其無狀態性(Stateless)。與傳統基於 Session 的驗證方式不同,JWT 讓伺服器無需儲存任何使用者狀態。所有必要的資訊(如使用者 ID、過期時間)都打包在 Token 內部,並透過伺服器獨有的密鑰進行簽名。每次 API 請求時,客戶端只需在 Header 中攜帶這個 Token,伺服器便能獨立驗證其有效性,大幅簡化了擴展性和伺服器資源管理。

這使得 JWT 特別適用於以下場景:

  • 分散式系統與微服務架構:多個服務可以共享同一個密鑰來驗證 Token,無需共用 Session 儲存。

  • 單頁應用程式(SPA)與行動應用:這些前端應用不需要依賴瀏覽器的 Cookie,可以直接在 HTTP Header 中傳送 Token。


在 Laravel 中實作 JWT 驗證

由於 Laravel 並未原生支援 JWT,我們通常會使用功能成熟的第三方套件,例如 tymon/jwt-auth。以下是實作的基本步驟:

1. 安裝與設定

首先,透過 Composer 安裝 tymon/jwt-auth 套件:

Bash
composer require tymon/jwt-auth

接著,發布套件設定檔並產生一組獨一無二的 JWT 密鑰,這組密鑰將用於 Token 的簽署與驗證。

Bash
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
php artisan jwt:secret

2. 模型配置

讓你的使用者模型 App\Models\User 實作 Tymon\JWTAuth\Contracts\JWTSubject 介面,並實作兩個方法:

PHP
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements JWTSubject
{
    // ...

    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims()
    {
        return [];
    }
}

getJWTIdentifier() 會告訴 JWT 套件要將哪個欄位作為 Token 的唯一識別碼,通常是使用者的主鍵(ID)。

3. 登入與保護路由

建立一個登入路由,讓使用者提供帳號密碼。驗證成功後,使用 auth()->attempt() 方法產生一個新的 Token 並回傳。

PHP
// 在控制器中
public function login(Request $request)
{
    $credentials = $request->only(['email', 'password']);

    if (!$token = auth()->attempt($credentials)) {
        return response()->json(['error' => 'Unauthorized'], 401);
    }

    return response()->json([
        'access_token' => $token,
        'token_type' => 'bearer',
        'expires_in' => auth()->factory()->getTTL() * 60
    ]);
}

對於需要驗證的路由,只需將其放入 auth:api 中介層即可。

PHP
Route::group(['middleware' => 'auth:api'], function () {
    // 這裡的路由需要帶上有效的 JWT 才能存取
    Route::get('me', 'UserController@me');
});

Token 刷新機制:兼顧安全與用戶體驗

由於 JWT 在過期前無法被「登出」,設定一個短效期的 Token(例如 15 分鐘)是個好的安全實踐。但這也帶來了另一個問題:使用者可能需要頻繁重新登入。為了解決這個問題,我們需要建立一個 Token 刷新機制。

策略一:使用內建的 auth()->refresh()

tymon/jwt-auth 套件提供了一個簡單的刷新方法。當 Access Token 過期時,客戶端可以帶上舊的 Token,發送刷新請求。

PHP
// 在控制器中
public function refresh()
{
    // 換取一個新的 Access Token
    $newToken = auth()->refresh();

    return response()->json([
        'access_token' => $newToken,
        'token_type' => 'bearer',
        'expires_in' => auth()->factory()->getTTL() * 60
    ]);
}

此方法會自動將舊的 Token 列入黑名單,確保其無法再次被使用,提升了安全性。

策略二:使用 Refresh Token 旋轉機制

這是一種更為進階和安全的策略。登入時,伺服器會發送兩個 Token:一個短效期的 Access Token 和一個長效期的 Refresh Token

  • Access Token:用於每次 API 請求,有效時間短,即使被盜用,風險也較低。

  • Refresh Token:用於換取新的 Access Token。

當 Access Token 過期時,客戶端會用 Refresh Token 發送刷新請求。伺服器驗證 Refresh Token 後,會生成一個全新的 Access Token 和一個全新的 Refresh Token,並將舊的 Refresh Token 註銷。這種「旋轉」機制能有效防止 Refresh Token 被重放攻擊,是業界推薦的最佳實踐。


安全性與最佳實踐

為了打造更強健的 API,我們還需考慮以下幾點:

  • Token 存放策略:為防禦 XSS 攻擊,不要將 Token 存放在瀏覽器的 localStorage。將其存放在 HttpOnlySecure 的 Cookie 中,可有效防止 JavaScript 腳本竊取 Token。

  • Token 盜用偵測:在 Token 的 Payload 中加入如使用者 IP 位址或 User Agent 等資訊,刷新時進行比對,若發現不一致則拒絕請求。

  • Logout 機制:實作一個登出路由,當使用者登出時,手動將其 Token 列入黑名單,使其立即失效,而非等待其自然過期。

綜合運用上述策略,我們不僅能利用 JWT 的優勢快速建立可擴展的 API 服務,也能確保其在各種應用場景下的安全性與穩定性。

沒有留言:

張貼留言

熱門文章