安裝:
使用最新的就行,使用時問題不在於版本,而在於你會不會讀原始碼。
$ 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
檔案中生成一個隨機的secret
。secret
要注意保管,不能洩露。.env
檔案在.gitignore
檔案中預設是被標記的,即.env
檔案是不建議上傳到 github 上,同樣也就不建議將secret
上傳到 github 上防止洩露。
$ php artisan jwt:secret
生成模型檔案並執行遷移:
生成模型檔案:
$ php artisan make:model DemoModel -m
-m
表示同時生成模型對應的遷移檔案。(資料庫遷移是個重要知識點,不懂的自行學習修改遷移檔案:
因為是學習/測試,因此我把程式最簡化,只需要name
和password
即可。執行遷移:
$ php artisan migrate
修改模型檔案:
修改內容:
必須
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)
進行驗證的原因。因為是學習/測試,因此我把程式最簡化,只需要
name
和password
即可。新增
implements JWTSubject
介面,並繼承以下方法:public function getJWTIdentifier() { return $this->getKey(); } public function getJWTCustomClaims() { return []; }
我理解的含義是,將
JWT
與Model
進行對映,因為我們需要使用JWT
生成token
,而生成時需要傳入JWTSubject
例項物件,但實際我們傳入的是App\Models\DemoModel
例項物件,比如JWTAuth::fromSubject(instance of JWTSubject)
。
修改 Config 配置檔案:
- 修改
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
。 - 修改
auth.php
配置檔案:
使用守衛為api
(因為我們前後端分離,路由是從/api
進入的);
將驅動者改為jwt
;既然我們要使用DemoModel
的eloquent
,那麼就要model=>App\Models\DemoModel::class
。
- 建立路由:在
routes/api.php
下
so easy! - 建立控制器:
$ 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()
能輸出更詳細的內容。
測試工具:postman
使用者註冊——服務端會生成
token
響應給客戶端(postman):
傳送 Post 請求,注意 url 中必須加上/api/
,這是因為我們使用的是routes/api.php
。不清楚api.php
和web.php
的區別可以百度。複製下面的
token
,不用複製引號。使用者登陸——客戶端(postman)會攜帶
token
給伺服器驗證:
把剛複製的token
貼上到這裡:
會響應true
,可以點選Preview
看的更清晰:
fromSubject(JWTSubject $subject)
:
- 生成一個
token
。需傳入的JWTSubject
物件就是一個Model
物件。
fromUser(JWTSubject $user)
:
fromSubject
的別名,呼叫fromUser
等於呼叫fromSubject
:
$token
:
- 當前
JWT
例項中儲存的token
物件,非string
型別。
getToken()
:
- 從當前
JWT
例項中獲取token
物件。 $token
為null
時會呼叫parseToken()
函式解析Request
中的token
,之後再呼叫setToken()
將解析後的token
轉換為token
物件,最後將其存入$this->token
中。
parseToken()
:
- 由上面的程式碼可知,第一步
$this->parseToken()
的返回值仍然是$this
。也就是說,parseToken()
函式的作用僅僅是給$this->token
賦予一個有意義的token
物件,當然如果賦予失敗,則$this->token = null
。
setToken($token)
:
- 檢查傳入的
Token物件|string
是否滿足【段數要求、令牌格式要求】,並將string
型別轉換成token
物件。setToken()
的返回值是$this
,而並非token
物件。 parseToken()
和setToken()
的區別在於parseToken()
會先從Request
中解析出token
。token
物件成員很簡單:$value
為string
型別的token
,get()
的作用就是返回value
。
該函式上面的註釋說Check that the token is valid.
,但直接使用JWTAuth::check()
時卻會發現始終返回false
,以為這是token
本身的問題(過期了呀什麼的)。我排查了很久才發現,這是因為發生異常時返回的是false
!而並沒有被進行丟擲,這可真是令人尷尬…
我把原始碼的false
改成$e
,並使用JWTAuth::check()
來驗證token
:
注意這裡不用var_dump()
或dd()
函式,直接return JWTAuth::check()
,這樣看錯誤會更清楚一些。
會發現錯誤是在執行Tymon\JWTAuth\JWT->requireToken()
這個函式時丟擲的。
找到它:
這不就是上面介紹的$this->token
嗎…
原來在進行JWTAuth::check()
前,我們需要對$this->token
進行賦值。我在上面介紹過,parseToken()
函式能給$this->token
賦值,那就來嘗試一下:
先把改過的原始碼改回來的:
$e
改回false
。
postman
返回了true
!
這裡使用了
var_dump
,不用會返回1,程式設計中true即1,false即0
我們再來測試一下錯誤的token
:
先把要請求的token
值隨意增刪改,
點選Send
:
返回了false
!
要呼叫驗證方法就得需要寫兩行程式碼,這明顯變得複雜了,因此我們通常使用JWTAuth::parseToken()->check()
一行程式碼來驗證請求發來的token
。
本作品採用《CC 協議》,轉載必須註明作者和本文連結