一直沒有好好看過jwt,直到前兩天要做web驗證,朋友給我推薦了jwt。才發現jwt已經被大家廣泛的應用了。看來我有點out了。哈哈,趁著這個世界來好好看看這個。
JWT(JSON Web Token), 顧名思義就是可以在Web上傳輸的token,這種token是用JSON格式進行format的。它是一個開源標準(RFC 7519),定義了一個緊湊的自包含的方式在不同實體之間安全的用JSON格式傳輸資訊。
由於現在很多專案都是前後端分離,restful api模式。所以傳統的session模式就沒有辦法滿足認證需求,這個時候jwt的作用就來了。可以說 restful api認證是jwt的一個很好的應用場景。
下面是一個很小的demo
<?php
require_once `src/JWT.php`;
header(`Content-type:application/json`);
//定義Key
const KEY = `dasjdkashdwqe1213dsfsn;p`;
$user = [
`uid`=>`dadsa-12312-vsd1s1-fsds`,
`account`=>`daisc`,
`password`=>`123456`
];
$redis = redis();
$action = $_GET[`action`];
switch ($action)
{
case `login`:
login();
break;
case `info`:
info();
break;
}
//登陸,寫入驗證token
function login()
{
global $user;
$account = $_GET[`account`];
$pwd = $_GET[`password`];
$res = [];
if($account==$user[`account`]&&$pwd==$user[`password`])
{
unset($user[`password`]);
$time = time();
$token = [
`iss`=>`http://test.cc`,//簽發者
`iat`=>$time,
`exp`=>$time+60,
`data`=>$user
];
$jwt = FirebaseJWTJWT::encode($token,KEY);
$res[`code`] = 200;
$res[`message`] = `登入成功`;
$res[`jwt`] = $jwt;
}
else
{
$res[`message`]= `使用者名稱或密碼錯誤`;
$res[`code`] = 401;
}
exit(json_encode($res));
}
function info()
{
$jwt = $_SERVER[`HTTP_AUTHORIZATION`] ?? false;
$res[`code`] = 200;
if($jwt)
{
$jwt = str_replace(`Bearer `,``,$jwt);
if(empty($jwt))
{
$res[`code`] = 401;
$res[`msg`] = `You do not have permission to access.`;
exit(json_encode($res));
}
try{
$token = (array) FirebaseJWTJWT::decode($jwt,KEY, [`HS256`]);
if($token[`exp`]<time())
{
$res[`code`] = 401;
$res[`msg`] = `登入超時,請重新登入`;
}
$res[`data`]= $token[`data`];
}catch (Exception $E)
{
$res[`code`] = 401;
$res[`msg`] = `登入超時,請重新登入.`;
}
}
else
{
$res[`code`] = 401;
$res[`msg`] = `You do not have permission to access.`;
}
exit(json_encode($res));
}
//連線redis
function redis()
{
$redis = new Redis();
$redis->connect(`127.0.0.1`);
return $redis;
}
這個dmeo裡面用jwt做了一個簡單的認證。 其中用到了一個php-jwt的加密包https://github.com/firebase/php-jwt
其中KEY
為定義的私鑰也就是jwt裡面的 sign
部分,這個一定要儲存好。
而header
部分php-jwt
包裡面已經幫我們完成了,加密程式碼如下
*/
public static function encode($payload, $key, $alg = `HS256`, $keyId = null, $head = null)
{
$header = array(`typ` => `JWT`, `alg` => $alg);
if ($keyId !== null) {
$header[`kid`] = $keyId;
}
if ( isset($head) && is_array($head) ) {
$header = array_merge($head, $header);
}
$segments = array();
$segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
$segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
$signing_input = implode(`.`, $segments);
$signature = static::sign($signing_input, $key, $alg);
$segments[] = static::urlsafeB64Encode($signature);
return implode(`.`, $segments);
}
可以看出預設的加密的方式是HS256
。這也是說jwt安全的原因。現階段HS256
加密還是很安全的。
這個包裡面也支援證照加密。
加密解密的過程這個包已經幫我們完成了。所以我們只需要定義jwt中的 poyload
部分就可以了。也就是demo裡面的token部分。加密成功會得到一個加密的Jwt字串,下次前端在請求api的時候需要攜帶這個jwt字串作為認證。
在header頭裡面增加Authorization
。在服務端驗證的時候回通過取得這個值來驗證回話的有效。
下面是poyload
的一些常用配置
$token = [
#非必須。issuer 請求實體,可以是發起請求的使用者的資訊,也可是jwt的簽發者。
"iss" => "http://example.org",
#非必須。issued at。 token建立時間,unix時間戳格式
"iat" => $_SERVER[`REQUEST_TIME`],
#非必須。expire 指定token的生命週期。unix時間戳格式
"exp" => $_SERVER[`REQUEST_TIME`] + 7200,
#非必須。接收該JWT的一方。
"aud" => "http://example.com",
#非必須。該JWT所面向的使用者
"sub" => "jrocket@example.com",
# 非必須。not before。如果當前時間在nbf裡的時間之前,則Token不被接受;一般都會留一些餘地,比如幾分鐘。
"nbf" => 1357000000,
# 非必須。JWT ID。針對當前token的唯一標識
"jti" => `222we`,
# 自定義欄位
"GivenName" => "Jonny",
# 自定義欄位
"name" => "Rocket",
# 自定義欄位
"Email" => "jrocket@example.com",
];
裡面包含的配置可以自由配置,也可以自己新增一些其他的。這些都是網上大家常用的,可以說是一種約定吧。
對於jwt
還有很多有疑問的地方,下來在慢慢研究,比如續期以及退出的問題