JWT 完整使用詳解

skyArony發表於2018-05-08

2018/5/7

  • 說實話,官方文件也是相當的亂,這次根據文件並檢視原始碼實驗了很多地方,大改了一次。

2018/10/4

  • 結合大家提出的問題和我近期新的理解,寫了一篇新文章,講的比較深,大家可以去看看 JWT 超詳細分析。本文這次主要修改了一些細節,對大家提出的問題整理寫到文章中。

本文是以 1.0.0-rc 為基準的。

JWT 全稱 JSON Web Tokens ,是一個非常輕巧的規範。這個規範允許我們使用JWT在使用者和伺服器之間傳遞安全可靠的資訊。它的兩大使用場景是:認證和資料交換。

資料

先擺出幾個參考資料,可以把連線都開啟,方便查閱:

Laravel

1. 使用 composer 安裝

# 建議使用1.0以上版本
composer require tymon/jwt-auth 1.*@rc

2. 進行一些配置

這裡指的注意的是,有些文件會說要新增 Tymon\JWTAuth\Providers\LaravelServiceProvider::class ,這隻在 Laravel 5.4 及以下版本是必要的,更新的 Laravel 版本無需新增。

還有一些文件說要新增 Tymon\JWTAuth\Providers\JWTAuthServiceProvider 這是很久以前的 JWT 版本的(大概0.5.3 以前的版本)。

2.1 釋出配置檔案

# 這條命令會在 config 下增加一個 jwt.php 的配置檔案
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

2.2 生成加密金鑰

# 這條命令會在 .env 檔案下生成一個加密金鑰,如:JWT_SECRET=foobar
php artisan jwt:secret

2.3 更新你的模型

如果你使用預設的 User 表來生成 token,你需要在該模型下增加一段程式碼

<?php

namespace App;

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

class User extends Authenticatable implements JWTSubject    # 這裡別忘了加
{
    use Notifiable;

    // Rest omitted for brevity

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

2.4 註冊兩個 Facade

這兩個 Facade 並不是必須的,但是使用它們會給你的程式碼編寫帶來一點便利。

config/app.php

'aliases' => [
        ...
        // 新增以下兩行
        'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth',
        'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory',
],

如果你不使用這兩個 Facade,你可以使用輔助函式 auth()

auth() 是一個輔助函式,返回一個guard,暫時可以看成 Auth Facade。

對於它有很多有必要說的,可以看我單獨寫的一篇文章——Laravel 輔助函式 auth 與 JWT 擴充套件詳解

// 如果你不用 Facade,你可以這麼寫
auth('api')->refresh();
// 用 JWTAuth Facade
JWTAuth::parseToken()->refresh();

兩個 Facede 常用可使用方法,可以看文章後面的附錄。

2.5 修改 auth.php

config/auth.php

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

    'api' => [
        'driver' => 'jwt',      // 原來是 token 改成jwt
        'provider' => 'users',
    ],
],

2.6 註冊一些路由

注意:在 Laravel 下,route/api.php 中的路由預設都有字首 api

Route::group([

    'prefix' => 'auth'

], function ($router) {

    Route::post('login', 'AuthController@login');
    Route::post('logout', 'AuthController@logout');
    Route::post('refresh', 'AuthController@refresh');
    Route::post('me', 'AuthController@me');

});

2.7 建立 token 控制器

php artisan make:controller AuthController

AuthController

值得注意的是 Laravel 這要用 auth('api') ,至於為什麼,我另一篇關於 JWT 擴充套件詳解的文章裡有講。

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;

class AuthController extends Controller
{
    /**
     * Create a new AuthController instance.
     * 要求附帶email和password(資料來源users表)
     * 
     * @return void
     */
    public function __construct()
    {
        // 這裡額外注意了:官方文件樣例中只除外了『login』
        // 這樣的結果是,token 只能在有效期以內進行重新整理,過期無法重新整理
        // 如果把 refresh 也放進去,token 即使過期但仍在重新整理期以內也可重新整理
        // 不過重新整理一次作廢
        $this->middleware('auth:api', ['except' => ['login']]);
        // 另外關於上面的中介軟體,官方文件寫的是『auth:api』
        // 但是我推薦用 『jwt.auth』,效果是一樣的,但是有更加豐富的報錯資訊返回
    }

    /**
     * Get a JWT via given credentials.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function login()
    {
        $credentials = request(['email', 'password']);

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

        return $this->respondWithToken($token);
    }

    /**
     * Get the authenticated User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function me()
    {
        return response()->json(auth('api')->user());
    }

    /**
     * Log the user out (Invalidate the token).
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        auth('api')->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    /**
     * Refresh a token.
     * 重新整理token,如果開啟黑名單,以前的token便會失效。
     * 值得注意的是用上面的getToken再獲取一次Token並不算做重新整理,兩次獲得的Token是並行的,即兩個都可用。
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        return $this->respondWithToken(auth('api')->refresh());
    }

    /**
     * Get the token array structure.
     *
     * @param  string $token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth('api')->factory()->getTTL() * 60
        ]);
    }
}

Lumen

1. 使用 composer 安裝

上面是用命令列安裝的,這裡用 composer.json 安裝。

// 我當時可用的版本是這個
"tymon/jwt-auth": "1.*@rc"

執行

composer update

2. 進行一些配置

2.1 開啟 Facade 和 Eloquent

取消以下行的註釋。

bootstrap/app.php

// $app->withFacades();

// $app->withEloquent();

2.2 開啟中介軟體認證

取消以下行的註釋。

bootstrap/app.php

// $app->routeMiddleware([
//     'auth' => App\Http\Middleware\Authenticate::class,
// ]);

// $app->register(App\Providers\AuthServiceProvider::class);

2.3 新增服務提供者

bootstrap/app.php

// 有些文件裡是說新增 Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,那是舊版本的
$app->register(\Tymon\JWTAuth\Providers\LumenServiceProvider::class);

2.4 生成加密金鑰

這條命令會在 .env 檔案下生成一個加密金鑰,如:JWT_SECRET=foobar

php artisan jwt:secret

2.5 更新你的模型

如果你使用預設的 User 表來生成 token,你需要在該模型下增加一段程式碼

<?php

namespace App;

use Illuminate\Auth\Authenticatable;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject
{
    use Authenticatable, Authorizable;

    ...

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

2.6 註冊兩個 Facade

Lumen 中沒有輔助函式 auth(),這兩個 Facade 就挺有用了。

bootstrap/app.php

把原先去了註釋的那一行再改一下。

$app->withFacades(true, [
    'Tymon\JWTAuth\Facades\JWTAuth' => 'JWTAuth',
    'Tymon\JWTAuth\Facades\JWTFactory' => 'JWTFactory',
]);

2.7 設定 auth.php

\vendor\laravel\lumen-framework\config\auth.php 也複製到 專案根目錄config 資料夾(沒有就新建)。

文件中有提到 Lumen 風格的配置檔案這個概念

指的就是都在 .env 檔案中設定各種設定項,在 \vendor\laravel\lumen-framework\config 資料夾下面的其他配置檔案中也可以看到,很多配置項都有 env(設定項key, 預設值) 這個方法,有這個配置項的就可以在 .env 檔案中設定 設定項=你的設定值 這樣設定。

而複製到根目錄 config 資料夾是 Laravel 風格的配置檔案實現方式

這裡我本來想盡量按 Lumen 風格實現的,但是下面這些屬性預設並沒有 env() 方式實現,所以我還是複製到根目錄下改算了。

auth.php

按下面進行新增或修改。

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

...

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => \App\User::class
]

2.8 註冊一些路由

Route::group([

    'prefix' => 'auth'

], function ($router) {

    Route::post('login', 'AuthController@login');
    Route::post('logout', 'AuthController@logout');
    Route::post('refresh', 'AuthController@refresh');
    Route::post('me', 'AuthController@me');

});

2.9 建立 token 控制器

Lumen 還精簡了很多輔助函式,比如 auth 和 bcrypt 等。

可以安裝 cosmicvelocity/lumen-helpersalbertcht/lumen-helpers 補全(建議用後者,更好安裝)

或者使用上面說的兩個 Facade。

AuthController.php

如果你沒使用擴充套件補充的輔助函式,你需要這麼寫,不然直接用上面的 Laravel 那個

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Tymon\JWTAuth\Facades\JWTAuth;

class AuthController extends Controller
{
    /**
     * Create a new AuthController instance.
     *
     * @return void
     */
    public function __construct()
    {
        // 這裡額外注意了:官方文件樣例中只除外了『login』
        // 這樣的結果是,token 只能在有效期以內進行重新整理,過期無法重新整理
        // 如果把 refresh 也放進去,token 即使過期但仍在重新整理期以內也可重新整理
        // 不過重新整理一次作廢
        $this->middleware('auth:api', ['except' => ['login']]);
        // 另外關於上面的中介軟體,官方文件寫的是『auth:api』
        // 但是我推薦用 『jwt.auth』,效果是一樣的,但是有更加豐富的報錯資訊返回
    }

    /**
     * Get a JWT via given credentials.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function login(Request $request)
    {
        $credentials = $request->only('email', 'password');

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

        return $this->respondWithToken($token);
    }

    /**
     * Get the authenticated User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function me()
    {
        return response()->json(JWTAuth::parseToken()->touser());
    }

    /**
     * Log the user out (Invalidate the token).
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        JWTAuth::parseToken()->invalidate();

        return response()->json(['message' => 'Successfully logged out']);
    }

    /**
     * Refresh a token.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        return $this->respondWithToken(JWTAuth::parseToken()->refresh());
    }

    /**
     * Get the token array structure.
     *
     * @param  string $token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => JWTAuth::factory()->getTTL() * 60
        ]);
    }
}

關於中介軟體

1.0 版本以上的 jwt-auth,中介軟體在服務提供者中已經定義了,所以不需要額外寫,按上面來即可。

下面是可用的中介軟體,第一二個功能一樣,但是第二個不會丟擲錯誤,第三四個功能一樣,沒什麼區別。

tymon\jwt-auth\src\Providers\AbstractServiceProvider.php

protected $middlewareAliases = [
    'jwt.auth' => Authenticate::class,
    'jwt.check' => Check::class,
    'jwt.refresh' => RefreshToken::class,
    'jwt.renew' => AuthenticateAndRenew::class,
];

1. token 的獲取、使用、刪除和重新整理

  • 以下用 postman 演示,{{TEST}} 為 postman 全域性變數:test.yfree.ccc
  • Laravel 環境下寫在 api.php 中的路由預設有字首 api
  • 下面的圖是 Lumen 環境的,沒有預設區字首 api

1.1 獲取 token

獲取token

JWT 完整使用詳解

1.2 使用 token

有兩種使用方法:

  • 加到 url 中:?token=你的token
  • 加到 header 中,建議用這種,因為在 https 情況下更安全:Authorization:Bearer 你的token

使用 token
JWT 完整使用詳解

新增中介軟體保護的就需要使用 token進行訪問

可以使用的中介軟體有 auth、auth:api、jwt.auth、jwt.refresh、jwt.check、jwt.renew

關於這些中介軟體之間有什麼差別,可以看我的另一篇文章:Laravel 輔助函式 auth 與 JWT 擴充套件詳解

1.3 刪除 token

刪除 token
JWT 完整使用詳解

刪除 token 後,token就會失效,無法再利用其獲取資料。

1.4 重新整理 token

重新整理 token
JWT 完整使用詳解

重新整理後,舊 token 將會失效,但是你可以設定一個寬限時間,這個在後面具體說。

2. token 的組成、建立以及解析

2.1 組成

一個 JWT token 是一個字串,它由三部分組成,頭部、載荷與簽名,中間用 . 分隔,例如:xxxxx.yyyyy.zzzzz

頭部(header)

頭部通常由兩部分組成:令牌的型別(即JWT)和正在使用的簽名演算法(如HMAC SHA256 或 RSA.)。
例如:

{
  "alg": "HS256",
  "typ": "JWT"
}

然後用 Base64Url 編碼得到頭部,即 xxxxx

載荷(Payload)

載荷中放置了 token 的一些基本資訊,以幫助接受它的伺服器來理解這個 token。同時還可以包含一些自定義的資訊,使用者資訊交換。

載荷的屬性也分三類:

  • 預定義(Registered)
  • 公有(public)
  • 私有(private)

預定義的載荷

{
  "sub": "1",
  "iss": "http://localhost:8000/auth/login",
  "iat": 1451888119,
  "exp": 1454516119,
  "nbf": 1451888119,
  "jti": "37c107e4609ddbcc9c096ea5ee76c667",
  "aud": "dev"
}

這裡面的前 7 個欄位都是由官方所定義的,也就是預定義(Registered claims)的,並不都是必需的。

  • iss (issuer):簽發人
  • sub (subject):主題
  • aud (audience):受眾
  • exp (expiration time):過期時間
  • nbf (Not Before):生效時間,在此之前是無效的
  • iat (Issued At):簽發時間
  • jti (JWT ID):編號

公有的載荷

在使用 JWT 時可以額外定義的載荷。為了避免衝突,應該使用 IANA JSON Web Token Registry 中定義好的,或者給額外載荷加上類似名稱空間的唯一標識。

私有載荷

在資訊互動的雙方之間約定好的,既不是預定義載荷也不是公有載荷的一類載荷。這一類載荷可能會發生衝突,所以應該謹慎使用。

將上面的 json 進行 Base64Url 編碼得到載荷,,即 yyyyy

關於載荷的理解:

這裡三種載荷的定義應該明確的一點是 —— 對於後兩種載荷,它並非定義了載荷的種類,然後讓你去選用哪種載荷,而是對你可能會定義出來的載荷做一個分類。

比如你定義了一個 admin 載荷,這個載荷按其分類應該是私有載荷,可能會和其他人定義的發生衝突。但如果你加了一個字首(名稱空間),如 namespace-admin,那麼這應該就算一個公有載荷了。(但其實標準並沒有定義怎麼去宣告名稱空間,所以嚴格來說,還是可能會衝突)

但是在現實中,團隊都是約定好的了要使用的載荷,這樣的話,好像根本不存在衝突的可能。那為什麼文件要這麼定義呢?我的理解是,RFC 是提出一種技術規範,出發點是一套通用的規範,考慮的範圍是所有開發者,而不僅僅侷限於一個開發者團隊。就像用 token 做認證已經是很常見的技術了,但是 JWT 的提出就相當於提出了一套較為通用的技術規範。既然是為了通用,那麼考慮在大環境下的衝突可能性也是必須的。

簽名(Signature)

簽名時需要用到前面編碼過的兩個字串,如果以 HMACSHA256 加密,就如下:

HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    secret
)

加密後再進行 base64url 編碼最後得到的字串就是 token 的第三部分 zzzzz

組合便可以得到 token:xxxxx.yyyyy.zzzzz

簽名的作用:保證 JWT 沒有被篡改過,原理如下:

HMAC 演算法是不可逆演算法,類似 MD5 和 hash ,但多一個金鑰,金鑰(即上面的secret)由服務端持有,客戶端把 token 發給服務端後,服務端可以把其中的頭部和載荷再加上事先共享的 secret 再進行一次 HMAC 加密,得到的結果和 token 的第三段進行對比,如果一樣則表明資料沒有被篡改。

Hash-based Message Authentication Code

PHP 程式碼示例

// 這裡要開啟true
$zzzzz = $this->base64url_encode(hash_hmac('sha256', 'xxxxx.yyyyy', getenv('JWT_SECRET'), true));

protected function base64url_encode($data) {
    return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

2.2 token 的建立

前面的 AuthController.php 中有兩行展現了這一種 token 的建立方法,即用使用者所給的賬號和密碼進行嘗試,密碼正確則用對應的 User 資訊返回一個 token

token 的建立方法不止這一種,接下來介紹 token 的三種建立方法:

  • 基於賬密引數
  • 基於 users 模型返回的例項
  • 基於 users 模型中的使用者主鍵 id

a) 基於賬密引數

這就是剛剛說的哪一種,貼出具體程式碼。

// 使用輔助函式
$credentials = request(['email', 'password']); 
$token = auth()->attempt($credentials)

// 使用 Facade
$credentials = $request->only('email', 'password');
$token = JWTAuth::attempt($credentials);

b) 基於 users 模型返回的例項

// 使用輔助函式
$user = User::first();
$token = auth()->login($user);

// 使用 Facade
$user = User::first();
$token = JWTAuth::fromUser($credentials);

c) 基於 users 模型中的主鍵 id

// 使用輔助函式
$token = auth()->tokenById(1);

// 使用 Facade
原始碼中沒找到

2.3 token 的解析

a) 解析 token 到物件

只有 Facade 需要這樣。

// 把請求傳送過來的直接解析到物件
JWTAuth::parseToken();

b) 獲取 token 中的 user 資訊

// 輔助函式
$user = auth()->user();

// Facade
$user = JWTAuth::parseToken()->authenticate();

c) 獲取 token

如果 token 被設定則會返回,否則會嘗試使用方法從請求中解析 token ,如果token未被設定或不能解析最終返回false。

// 輔助函式
$token = auth()->getToken();

// Facade
$token = JWTAuth::parseToken()->getToken();

更多方法可以看文章後面的附錄。

d) 如果是前端

直接 base64 解碼 token 的前兩段即可以知道所需的資訊。

3. 載荷的設定和獲取

a) 載荷設定

載荷資訊會在 token 解碼時得到,同時越大的陣列會生成越長的 token ,所以不建議放太多的資料。同時因為載荷是用 Base64Url 編碼,所以相當於明文,因此絕對不能放密碼等敏感資訊。

$customClaims = ['foo' => 'bar', 'baz' => 'bob'];

// 輔助函式
$token = auth()->claims($customClaims)->attempt($credentials);

// Facade - 1
$token = JWTAuth::claims($customClaims)->attempt($credentials);

--- 下面兩種試了好像不行,不過前面的夠用了

// Facade - 2
$payload = JWTFactory::make($customClaims);
$token = JWTAuth::encode($payload);

// Facade - 3
$payload = JWTFactory::sub(123)->aud('foo')->foo(['bar' => 'baz'])->make();
$token = JWTAuth::encode($payload);

b) 載荷解析

從請求中把載荷解析出來。可以去看擴充套件原始碼,裡面還有很多的方法。

// 輔助函式
$exp = auth()->payload()->get('exp');
$json = auth()->payload()->toJson();
$array = auth()->payload()->jsonSerialize();
$sub = $array['sub'];

// Facade - 1
$payload = JWTAuth::parseToken()->getPayload();
$payload->get('sub'); // = 123
$payload['jti']; // = 'asfe4fq434asdf'
$payload('exp') // = 123456
$payload->toArray(); // = ['sub' => 123, 'exp' => 123456, 'jti' => 'asfe4fq434asdf'] etc

// Facade - 2
$exp = JWTAuth::parseToken()->getClaim('exp');

4. token 的三個時間

一個 token 一般來說有三個時間屬性,其配置都在 config/jwt.php 內。

有效時間

有效時間指的的是你獲得 token 後,在多少時間內可以憑這個 token 去獲取內容,逾時無效。

// 單位:分鐘
'ttl' => env('JWT_TTL', 60)

重新整理時間

重新整理時間指的是在這個時間內可以憑舊 token 換取一個新 token。例如 token 有效時間為 60 分鐘,重新整理時間為 20160 分鐘,在 60 分鐘內可以通過這個 token 獲取新 token,但是超過 60 分鐘是不可以的,然後你可以一直迴圈獲取,直到總時間超過 20160 分鐘,不能再獲取。

這裡過期後能否重新整理,經 [[@Rootrl](https://learnku.com/users/433)](https://learnku.com/users/433) 指出,其實並不是這麼絕對,具體細節,看我們上面 AuthController 處的程式碼。有詳細補充
這也是一個 token 被加入黑名單之後,會存在的時間

// 單位:分鐘
'refresh_ttl' => env('JWT_REFRESH_TTL', 20160)

寬限時間

寬限時間是為了解決併發請求的問題,假如寬限時間為 0s ,那麼在新舊 token 交接的時候,併發請求就會出錯,所以需要設定一個寬限時間,在寬限時間內,舊 token 仍然能夠正常使用。

// 寬限時間需要開啟黑名單(預設是開啟的),黑名單保證過期token不可再用,最好開啟
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true)

// 設定寬限時間,單位:秒
'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 60)

5. 關於 JWT 的討論

5.1 為什麼用 JWT?

看我的新文章 JWT 超詳細分析

5.2 token 的重新整理問題?

a) token 為什麼要重新整理嗎?

首先 Basic Auth 是一種最簡單的認證方法,但是由於每次請求都帶使用者名稱和密碼,頻繁的傳輸肯定不安全,所以才有 cookiessession 的運用。如果 token 不重新整理,那麼 token 就相當於上面的使用者名稱+密碼,只要獲取到了,就可以一直盜用,因此 token 設定有效期並能夠進行重新整理是必要的。

b) token 有效期多久合適,重新整理頻率多久合適?

有效期越長,風險性越高,有效性越短,重新整理頻率越高,重新整理就會存在重新整理開銷,所以這需要綜合考慮。而且 web 端應該設定為分鐘級和小時級,而移動端應該設定為天級和周級。

c) 有沒有必要每次都重新整理 token ?

看我的新文章 JWT 超詳細分析

1. JWT 的 兩個 Facade

1.1 JWTAuth

JWTAuth::parseToken()->方法() 一般都可以換成 auth()->方法()

token 生成

attempt

根據 user 賬密新建一個 token。

$credentials = $request->only('email', 'password');
$token = JWTAuth::attempt($credentials);

fromUser or fromSubject

根據 user 物件生成一個 token。後者是前者別名。

$user = User::find(1);
$token = JWTAuth::fromUser($user);

token 控制

refresh

更新 token。

$newToken = JWTAuth::parseToken()->refresh();

invalidate

讓一個 token 無效。

JWTAuth::parseToken()->invalidate();

check

檢驗 token 的有效性。

if(JWTAuth::parseToken()->check()) {
    dd("token是有效的");
}

token 解析

authenticate or toUser or user

這三個效果是一樣的,toUserauthenticate 的別名,而 user 比前兩者少一個 user id 的校驗,但並沒有什麼影響。

$user = JWTAuth::parseToken()->toUser();

parseToken

從 request 中解析 token 到物件中,以便進行下一步操作。

JWTAuth::parseToken();

getToken

從 request 中獲取token。

$token = JWTAuth::getToken();  // 這個不用 parseToken ,因為方法內部會自動執行一次

載荷控制

customClaims or claims

設定載荷的 customClaims 部分。後者是前者的別名。

$customClaims = ['sid' => $sid, 'code' => $code];
$credentials = $request->only('email', 'password');
$token = JWTAuth::customClaims($customClaims)->attempt($credentials);

getCustomClaims

獲取載荷的 customClaims 部分,返回一個陣列。

$customClaims = JWTAuth::parseToken()->getCustomClaims()

getPayload or payload

獲取所有載荷,三個都是一樣的,最後一個一般用來檢驗 token 的有效性

$payload = JWTAuth::parseToken()->payload();

// then you can access the claims directly e.g.
$payload->get('sub'); // = 123
$payload['jti']; // = 'asfe4fq434asdf'
$payload('exp') // = 123456
$payload->toArray(); // = ['sub' => 123, 'exp' => 123456, 'jti' => 'asfe4fq434asdf'] etc

getClaim

獲取載荷中指定的一個元素。

$sub = JWTAuth::parseToken()->getClaim('sub');

1.2 JWTGuard

這個 Facade 主要進行載荷的管理,返回一個載荷物件,然後可以通過 JWTAuth 來對其生成一個 token。

// 載荷的高度自定義
$payload = JWTFactory::sub(123)->aud('foo')->foo(['bar' => 'baz'])->make();
$token = JWTAuth::encode($payload);
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
$payload = JWTFactory::make($customClaims);
$token = JWTAuth::encode($payload);

1.3 其他一些用法

這裡用 auth 的寫法,因為 Laravel 有多個 guard,預設 guard 也不是 api ,所以需要寫成 auth('api') 否則,auth() 即可。

設定載荷

$token = auth('api')->claims(['foo' => 'bar'])->attempt($credentials);

顯示設定 token

$user = auth('api')->setToken('eyJhb...')->user();

顯示設定請求

$user = auth('api')->setRequest($request)->user();

重寫有效時間

$token = auth('api')->setTTL(7200)->attempt($credentials);

驗證賬密是否正確

$boolean = auth('api')->validate($credentials);
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章