tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)

long0發表於2020-12-18

安裝:

使用最新的就行,使用時問題不在於版本,而在於你會不會讀原始碼。

$ composer require tymon/jwt-auth

生成配置檔案:

會在config資料夾中生成一個jwt.php檔案。
jwt.php可以配置一些加密演算法、token過期時間等資訊。

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

生成加密金鑰:

token是由 header(base64演算法加密) + payload(base64演算法加密) + secret 再用HS256等演算法加密後組成的。
secret是伺服器儲存的,下面命令會在.env檔案中生成一個隨機的secretsecret要注意保管,不能洩露。
.env檔案在.gitignore檔案中預設是被標記的,即.env檔案是不建議上傳到 github 上,同樣也就不建議將secret上傳到 github 上防止洩露。

$ php artisan jwt:secret

生成模型檔案並執行遷移:

  1. 生成模型檔案:

    $ php artisan make:model DemoModel -m

    -m表示同時生成模型對應的遷移檔案。(資料庫遷移是個重要知識點,不懂的自行學習

  2. 修改遷移檔案:
    因為是學習/測試,因此我把程式最簡化,只需要 namepassword 即可。
    對 jwt 使用的簡單刨析(自定義Model)

  3. 執行遷移:

    $ php artisan migrate

修改模型檔案:

對 jwt 使用的簡單刨析(自定義Model)

修改內容:

  1. 必須 extends User這裡 as Authenticatable了。
    User類繼承了許多介面,比如Authenticatable介面,JWT有部分功能使用到了 laravel 自帶的Auth功能,比如JWTAuth::attempt()函式,這個函式是JWTAuth類呼叫的,而並不是Auth呼叫,但它的呼叫卻需要 Authenticatable 類例項,因此如果不 extends User的話,會丟擲validateCredentials() must be an instance of Illuminate\Contracts\Auth\Authenticatable異常。
    這也是為什麼可以直接使用auth("api")->attempt(xxx)進行驗證的原因。
    對 jwt 使用的簡單刨析(自定義Model)

  2. 因為是學習/測試,因此我把程式最簡化,只需要 namepassword 即可。

  3. 新增implements JWTSubject介面,並繼承以下方法:

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

    我理解的含義是,將 JWTModel 進行對映,因為我們需要使用JWT生成token,而生成時需要傳入JWTSubject例項物件,但實際我們傳入的是 App\Models\DemoModel 例項物件,比如JWTAuth::fromSubject(instance of JWTSubject)

修改 Config 配置檔案:

  1. 修改app.php配置檔案:
    aliases新增以下兩個欄位:
         'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
         'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
    新增該別名的好處是,能直接在頭部use JWTAuth;便可使用JWTAuth::XXX,而無需use Tymon\JWTAuth\Facades\JWTAuth;長長的 path 。但你use JWTAuth;時會發現使用JWTAuth::XXX vscode 編輯器會報錯,這是因為編輯器並沒有識別別名的特性,在報錯下直接發請求,也是可以正常響應的。
    如果想直接use 長長的 path,可以不用新增這兩行別名,因為有些編輯器會自動引入use XXX
  2. 修改auth.php配置檔案:
    對 jwt 使用的簡單刨析(自定義Model)
    對 jwt 使用的簡單刨析(自定義Model)
    使用守衛為api(因為我們前後端分離,路由是從/api進入的);
    將驅動者改為jwt;既然我們要使用DemoModeleloquent,那麼就要model=>App\Models\DemoModel::class
  1. 建立路由:在routes/api.php
    對 jwt 使用的簡單刨析(自定義Model)
    so easy!
  2. 建立控制器:
    $ php artisan controller:DemoController
<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\DemoModel;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Facades\JWTAuth;

class DemoController extends Controller
{
    public function register(Request $request)
    {
        $credentials = [
            'name' => $request->name,
            'password' => $request->password
        ];

        $dm = DemoModel::create($credentials);
        if ($dm) {
            // 生成 token
            $token = JWTAuth::fromSubject($dm);
            return var_dump($token);
        }
    }

    public function login(Request $request)
    {
        // 驗證 token
        return var_dump((JWTAuth::parseToken()->check()));
    }
}

var_dump()dd()用法差不多,var_dump()能輸出更詳細的內容。

  1. 測試工具:postman

  2. 使用者註冊——服務端會生成token響應給客戶端(postman):
    傳送 Post 請求,注意 url 中必須加上/api/,這是因為我們使用的是routes/api.php。不清楚api.phpweb.php的區別可以百度。

    複製下面的token,不用複製引號。
    tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)

  3. 使用者登陸——客戶端(postman)會攜帶token給伺服器驗證:
    把剛複製的token貼上到這裡:
    tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)
    會響應true,可以點選Preview看的更清晰:
    tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)

fromSubject(JWTSubject $subject)

  1. 生成一個token。需傳入的JWTSubject物件就是一個Model物件。

tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)

fromUser(JWTSubject $user)

  1. fromSubject的別名,呼叫fromUser等於呼叫fromSubject:
    tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)

$token

  1. 當前JWT例項中儲存的token物件,非string型別。

getToken()

  1. 從當前JWT例項中獲取token物件。
  2. $tokennull時會呼叫parseToken()函式解析Request中的token,之後再呼叫setToken()將解析後的token轉換為token物件,最後將其存入$this->token中。
    tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)
    tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)
    tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)

parseToken()

  1. 由上面的程式碼可知,第一步$this->parseToken()的返回值仍然是$this也就是說parseToken()函式的作用僅僅是給$this->token賦予一個有意義的token物件,當然如果賦予失敗,則$this->token = null

setToken($token)

  1. 檢查傳入的Token物件|string是否滿足【段數要求、令牌格式要求】,並將string型別轉換成token物件。setToken()的返回值是$this,而並非token物件。
    tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)
  2. parseToken()setToken()的區別在於parseToken()會先從Request中解析出token
  3. token物件成員很簡單:$valuestring型別的tokenget()的作用就是返回value
    tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)

該函式上面的註釋說Check that the token is valid.,但直接使用JWTAuth::check()時卻會發現始終返回false,以為這是token本身的問題(過期了呀什麼的)。我排查了很久才發現,這是因為發生異常時返回的是false!而並沒有被進行丟擲,這可真是令人尷尬…
tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)
我把原始碼的false改成$e,並使用JWTAuth::check()來驗證token
tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)
tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)
注意這裡不用var_dump()dd()函式,直接return JWTAuth::check(),這樣看錯誤會更清楚一些。
tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)
會發現錯誤是在執行Tymon\JWTAuth\JWT->requireToken()這個函式時丟擲的。
找到它:
tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)
這不就是上面介紹的$this->token嗎…
原來在進行JWTAuth::check()前,我們需要對$this->token進行賦值。我在上面介紹過,parseToken()函式能給$this->token賦值,那就來嘗試一下:

先把改過的原始碼改回來的:$e改回false

tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)
postman返回了true
tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)

這裡使用了var_dump,不用會返回1,程式設計中true即1,false即0

我們再來測試一下錯誤token
先把要請求的token值隨意增刪改,
tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)
點選Send
tymon/jwt-auth 的簡單使用與淺度刨析(使用自定義Model)
返回了false

要呼叫驗證方法就得需要寫兩行程式碼,這明顯變得複雜了,因此我們通常使用
JWTAuth::parseToken()->check()一行程式碼來驗證請求發來的token

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章