【Laravel實戰】15分鐘學會如何實作API的驗證機制

【Laravel實戰】15分鐘學會如何實作API的驗證機制

準備工作

什麼是JWT

你或許知道,在Laravel中,網頁端預設是利用 session 識別登入者身分與權限,但是 API 內建並沒有合適的解決方案

那怎麼辦呢? 幸好有 JWT 可以幫我們

JWT 為 Json Web Token的簡稱,可為我們的 API 進行身分驗證。它本是以 JSON 為格式的輕量級標準,有別於 SAML (Security Assertion Markup Language) 這種屬於比較複雜的 XML 格式

JWT 組成

JWT 是一組字串,透過(.)來分成三個以 Base64 來進行編碼的部分:

  • Header:含 Token 的種類及產生簽章(signature)要使用的雜湊(Hash)演算法種類
  • Payload:存放備用的資訊,例如用戶資訊
  • Signature:編譯後的 Header、Payload 與密鑰透過指定的雜湊演算法所產生
// base64(Header) + base64(Payload) + base64(Signature)
// xxxxx.yyyyy.zzzzz
Header

Header 是包含定義 Token 種類(type)及雜湊演算法(alg)資訊的 JSON 資料。在此設定 Token 種類為 JWT,之後要使用雜湊演算法 HS256 來產生簽章(signature)。之後此 JSON 將被轉換成 Base64 編碼,成為 JWT 的第一個部分:

{
  "alg": "HS256", 
  "type": "JWT"
}
Payload

Payload 也是一個 JSON,使用者和相關的存放資訊都可以放置其中。通常會使用 exp 設定 Token 到期的時間,使用 iat 來設定 Token 簽發時間。之後此 JSON 將被轉換成 Base64 編碼,成為 JWT 的第二個部分

{
  "_id": "<user_id>", 
  "name": "Mike",
  "exp": 1300819380
}

Payload 又被稱為 claims,能想成使用者是透過附帶 JWT 通過認證,因此能說這筆資訊是屬於他(她)的

注意:

不要將隱私資訊存放在 Payload 當中。Payload 和 Header 被轉換成 Base64 編碼後,能夠被輕易的轉換回來,因此不應該把如用戶密碼等重要資料存取在 Payload 當中

簽章 Signature

簽章(Signature )是將被轉換成 Base64 編碼的 Header、Payload 與自己定義的密鑰,透過在 Header 設定的雜湊演算法方式所產生的

由於密鑰並非公開,因此後端在拿到 Token 後,能透過解碼,確認資料內容正確,且未被變更,以驗證對方身份

什麼是 Acess Token

你可以理解為 API 訪問後端時用來驗證的憑證。一般來說 API 的運作機制是像這樣的

流程 1.前端先透過登入 API 來進行登入

流程 2.登入驗證成功後,後端產生並回傳一組帶有資訊,且僅能在後端被驗證的 Token

流程 3.前端利用 Bearer 方式進行驗證,將 Token 夾帶在 Header 來造訪需要權限的 API

流程 4.當 Token 失效後,返回流程 1

其中 JWT 所提供的 Token 有效期預設為 60分鐘,當時間一過又或者是自行登出之後, Token 就會失效,必須重新取得

快速實作

Step 1.下載 JWT-Auth 套件

開啟 Terminal , 輸入以下指令

composer require tymon/jwt-auth:"^2.0"

(非必須)註冊供應器

如果是 Laravel 5.4 或以下的版本,需要自行註冊供應器到 Provider 列表

開啟 app.php ,修改 providers 陣列

//config\app.php

'providers' => [
    ...
                    Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
],

Step 2.發布套件設定檔

接著發布套件的設定檔,也就是名為 jwt.php 的設定檔,位於 config 資料夾

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

Step 3.生成 JWT 憑證

接著要在 .env 檔案去產生 JWT 憑證,若憑證為空,則被視為非法身分

php artisan jwt:secret

Step 4.修改 User 模型

重點在宣告 User 模型實作 JWTSubject 介面,並加入下列2個方法:

//app\Models\User.php

namespace App\Models;

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

class User extends Authenticatable implements JWTSubject
{
    use Notifiable;

    ... (略)

    /**
     * 取得 JWT 辨識字串
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * 回傳鍵值對陣列,內容包含被加入 JWT 的自定義 Payload
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

Step 5.修改驗證設定

修改 config\auth.php ,如下所示:

//config\auth.php

'defaults' => [
    'guard' => 'web', 
    'passwords' => 'users',
],

...

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

Step 6.加入驗證路由

修改 api.php ,加入以下路由:

//routes\api.php

Route::group(['prefix' => 'auth', 'namespace' => 'App\Http\Controllers\Api'], function () {
    Route::get('/', 'AuthController@me')->name('me');
    Route::post('login', 'AuthController@login')->name('login');
    Route::post('logout', 'AuthController@logout')->name('logout');
});

Step 7.建立驗證控制器

接著來生成 AuthController,開啟 Terminal ,輸入指令如下:

php artisan make:controller Api/AuthController

編輯 AuthController.php,內容如下:

//app\Http\Controllers\Api\AuthController.php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Tymon\JWTAuth\Exceptions\JWTException;

class AuthController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth:api')->except('login');
    }

    public function login()
    {
        $credentials = request(['email', 'password']);

        try {
            if (!$token =auth()->guard('api')->attempt($credentials)) {
                return response()->json(['status' => 0, 'message' => '無效的驗證資料'], 401);
            }
        } catch (JWTException $e) {
            return response()->json([
                'error' => '無法建立 Token'
            ], 500);
        }

        return response()->json(['status' => 1, 'token' => $token]);
    }

    public function me()
    {
        return response()->json(auth()->user());
    }

    public function logout()
    {
        Auth::logout();
        return response()->json(['status' => 1]);
    }
}

Step 8.測試API

API 已經撰寫完畢,是時候來測試看看了,你可以使用 Postman 來試看看之前撰寫的 API 是否正確

API參數對照表
URI description HTTP Method Header
api/auth 取得user資訊 GET Authorization: bearer Token Accept: application/json
api/auth/login 登入 POST
api/auth/logout 登出 POST Authorization: bearer Token Accept: application/json

當進行登入時需要傳入驗證資料,否則會爆出無效驗證

打開 Postman 後,使用取得 user 資訊的API,若不夾帶 token、token 失效或者使用非法 token 等這3種狀況,就會收到 HTTP Status Code 為 401 的回應

你或許感到好奇,為何我們會收到401的狀態碼呢?

因為這支 API 被宣告使用名為 auth:api 的中介層,倘若你身分是不合法的,就會被中介層拒於門外。這時我們就需要透過登入 API 來取得有效的 Token,並取得 user 資訊

就這樣,利用 JWT 進行 API 身分驗證的機制就完成了

Step 9.為隱私 API 加上 JWT 保護

最後一步就是要為所有需要保護的 API 路由加入 auth:api 這個中介層,請參考以下程式碼


Route::middleware('auth:api')->group(function(){
   //在裏頭擺放要被保護的路由們
     ...
});

參考資料


分享這篇文章:

訂閱電子報,索取 Laravel 學習手冊

價值超過 3000 元,包含常用 Laravel 語法與指令!

Laravel 百萬年薪特訓營

從最基礎的 PHP 語法開始,包含所有你該知道的網頁基礎知識,連同 Laravel 從零開始一直到實戰,最後還將告訴你如何找好工作,讓你及早擁有百萬年薪