php firebase/php-jwt token驗證(還在用session嗎?)

一:JWT介紹:全稱JSON Web Token,基於JSON的開放標準((RFC 7519) ,以token的方式代替傳統的Cookie-Session模式,用於各服務器、客戶端傳遞信息簽名驗證。

二:JWT優點:

1:服務端不需要保存傳統會話信息,沒有跨域傳輸問題,減小服務器開銷。

2:jwt構成簡單,佔用很少的字節,便於傳輸。

3:json格式通用,不同語言之間都可以使用。

三:JWT組成

1:jwt由三部分組成:

頭部(header)

載荷(payload) 包含一些定義信息和自定義信息

簽證(signature)

2:具體構成:

header:

{

"typ": "JWT", //聲明類型為jwt

"alg": "HS256" //聲明簽名算法為SHA256

}

載荷(payload)

{

"iss": "http://www.helloweba.net",

"aud": "http://www.helloweba.net",

"iat": 1525317601,

"nbf": 1525318201,

"exp": 1525318201,

"data": {

"userid": 1,

"username": "李小龍"

}

}

載荷包括兩部分:標準聲明和其他聲明。

標準聲明:JWT標準規定的聲明,但不是必須填寫的;

接收該JWT的一方

iss: jwt簽發者

sub: jwt所面向的用戶

aud: 接收jwt的一方

exp: jwt的過期時間,過期時間必須要大於簽發時間

nbf: 定義在什麼時間之前,某個時間點後才能訪問

iat: jwt的簽發時間

jti: jwt的唯一身份標識,主要用來作為一次性token。

其他聲明:自己定義的字段,因為這部分是可以解開的,建議不要加入敏感信息,這裡的data就是我自己定義的聲明

最終是這樣的:

php firebase/php-jwt token驗證(還在用session嗎?)

三部分分別以逗號隔開

四:使用:PHP有很多jwt包,包括比如:lcobucci/jwt,我這裡使用firebase。可以從 git地址 下載

1:把代碼拉取下來

composer require firebase/php-jwt

2:服務端簽發Token

use \Firebase\JWT\JWT; //導入JWT

class MainController extends Controller

{

//簽發Token

public function lssue()

{

$key = '344'; //key

$time = time(); //當前時間

$token = [

'iss' => 'http://www.helloweba.net', //簽發者 可選

'aud' => 'http://www.helloweba.net', //接收該JWT的一方,可選

'iat' => $time, //簽發時間

'nbf' => $time , //(Not Before):某個時間點後才能訪問,比如設置time+30,表示當前時間30秒後才能使用

'exp' => $time+7200, //過期時間,這裡設置2個小時

'data' => [ //自定義信息,不要定義敏感信息

'userid' => 1,

'username' => '李小龍'

]

];

echo JWT::encode($token, $key); //輸出Token

}

}

3:解析Token,為了演示,這裡直接寫。

use \Firebase\JWT\JWT; //導入JWT

class

MainController extends Controller

{

public function verification()

{

$key = '344'; //key要和簽發的時候一樣

$jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC93d3cuaGVsbG93ZWJhLm5ldCIsImF1ZCI6Imh0dHA6XC9cL3d3dy5oZWxsb3dlYmEubmV0IiwiaWF0IjoxNTI1MzQwMzE3LCJuYmYiOjE1MjUzNDAzMTcsImV4cCI6MTUyNTM0NzUxNywiZGF0YSI6eyJ1c2VyaWQiOjEsInVzZXJuYW1lIjoiXHU2NzRlXHU1YzBmXHU5Zjk5In19.Ukd7trwYMoQmahOAtvNynSA511mseA2ihejoZs7dxt0"; //簽發的Token

try {

JWT::$leeway = 60;//當前時間減去60,把時間留點餘地

$decoded = JWT::decode($jwt, $key, ['HS256']); //HS256方式,這裡要和簽發的時候對應

$arr = (array)$decoded;

print_r($arr);

} catch(\Firebase\JWT\SignatureInvalidException $e) { //簽名不正確

echo $e->getMessage();

}catch(\Firebase\JWT\BeforeValidException $e) { // 簽名在某個時間點之後才能用

echo $e->getMessage();

}catch(\Firebase\JWT\ExpiredException $e) { // token過期

echo $e->getMessage();

}catch(Exception $e) { //其他錯誤

echo $e->getMessage();

}

//Firebase定義了多個 throw new,我們可以捕獲多個catch來定義問題,catch加入自己的業務,比如token過期可以用當前Token刷新一個新Token

}

}

在src目錄下的JWT.php中的方法 decode中,可以看到作者是通過多個 自定義 throw new 拋出異常的

php firebase/php-jwt token驗證(還在用session嗎?)

所以我們可以根據不同的throw new設置多個catch來捕獲。

輸出的數據:

Array

(

[iss] => http://www.helloweba.net

[aud] => http://www.helloweba.net

[iat] => 1525340317

[nbf] => 1525340317

[exp] => 1525347517

[data] => stdClass Object

(

[userid] => 1

[username] => 李小龍

)

)

五:模擬實戰

1:方案:客戶端通過用戶名密碼登錄以後,服務端返回給客戶端兩個token:access_tokenrefresh_token

access_token:請求接口的token

refresh_token:刷新access_token

舉個例子:比如access_token設置2個小時過期,refresh_token設置7天過期,2小時候後,access_token過期,但是refresh_token還在7天以內,那麼客戶端通過refresh_token來服務端刷新,服務端重新生成一個access_token;如果refresh_token也超過了7天,那麼客戶端需要重新登錄獲取access_token和refresh_token。

為了區分兩個token,我們在載荷(payload)加一個字段 scopes :作用域。

access_token中設置:scopes:role_access

refresh_token中設置:scopes:role_refresh

有些人是用一個token,要麼設置很長時間過期;要麼設置幾個小時過期,如果過期,用過期的token去換取新的token,這種其實是有問題的,有些人說token有兩個時間,一個是過期時間,一個是刷新時間,只要在刷新時間內就可以換取新token。假如別人拿到了你這token,如果也在過期時間之內,不是同樣可以刷新token?除非兩個token都拿到,相對來說第二種更安全。

直接上代碼:

use \Firebase\JWT\JWT; //導入JWT

class MainController extends Controller

{

public function authorizations()

{

$key = 'ffdsfsd@4_45'; //key

$time = time(); //當前時間

//公用信息

$token = [

'iss' => 'http://www.helloweba.net', //簽發者 可選

'iat' => $time, //簽發時間

'data' => [ //自定義信息,不要定義敏感信息

'userid' => 1,

]

];

$access_token = $token;

$access_token['scopes'] = 'role_access'; //token標識,請求接口的token

$access_token['exp'] = $time+7200; //access_token過期時間,這裡設置2個小時

$refresh_token = $token;

$refresh_token['scopes'] = 'role_refresh'; //token標識,刷新access_token

$refresh_token['exp'] = $time+(86400 * 30); //access_token過期時間,這裡設置30天

$jsonList = [

'access_token'=>JWT::encode($access_token,$key),

'refresh_token'=>JWT::encode($refresh_token,$key),

'token_type'=>'bearer' //token_type:表示令牌類型,該值大小寫不敏感,這裡用bearer

];

Header("HTTP/1.1 201 Created");

echo json_encode($jsonList); //返回給客戶端token信息

}

}

看起來輸出是這樣的:

{

"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC93d3cuaGVsbG93ZWJhLm5ldCIsImlhdCI6MTUyNTQxNzg5NywiZGF0YSI6eyJ1c2VyaWQiOjF9LCJzY29wZXMiOiJyb2xlX2FjY2VzcyIsImV4cCI6MTUyNTQyNTA5N30.Nxp1yutwt8Fxj5XEzes4j-X4tCBQwE0htEV3Msm2D8s",

"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC93d3cuaGVsbG93ZWJhLm5ldCIsImlhdCI6MTUyNTQxNzg5NywiZGF0YSI6eyJ1c2VyaWQiOjF9LCJzY29wZXMiOiJyb2xlX3JlZnJlc2giLCJleHAiOjE1MjgwMDk4OTd9.YY8Lid3nk3bnV-ZnAYneKJxGiaD73waqTpC3bHz3wsY",

"token_type": "bearer"

}

http頭部響應是這樣的:兩個token客戶端保存,一個用來取接口,一個用來刷新接口。

php firebase/php-jwt token驗證(還在用session嗎?)

客戶端請求的時候在http頭部攜帶 Authorization: bearer token,注意bearer後面有個空格,我們用PHP模擬一下。

php firebase/php-jwt token驗證(還在用session嗎?)

請求信息是這樣的,PHP獲取Authorization信息判斷。

這個只是一種思路,大家可以按照自己的思路去弄,這種思路服務端不用保存token,但如果涉及到token的註銷就必須保存,比如token沒過期,但是要註銷token,這時候可以上redis。

最後請務必使用HTTPS


分享到:


相關文章: