什么是JWT
JSON WEB Token,是一種基于JSON的莫其、用于在網(wǎng)絡(luò)上聲明某種主張的令牌(token)。JWT通常由三部分組成: 頭信息(header), 消息體(payload)和簽名(signature)积锅。
1.Header 頭信息通常包含兩部分,type:代表token的類(lèi)型摔桦,這里使用的是JWT類(lèi)型。 alg:使用的Hash算法,例如HMAC SHA256或RSA.
{
"alg": "HS256",
"typ": "JWT"
}
2.Payload 一個(gè)token的第二個(gè)部分是荷載信息,它包含一些聲明Claim(實(shí)體的描述睬魂,通常是一個(gè)User信息,還包括一些其他的元數(shù)據(jù))
聲明分三類(lèi):
1.Reserved Claims,這是一套預(yù)定義的聲明镀赌,并不是必須的,這是一套易于使用氯哮、操作性強(qiáng)的聲明。包括:iss(issuer)商佛、exp(expiration time)喉钢、sub(subject)姆打、aud(audience)等
2.Plubic Claims,
3.Private Claims,交換信息的雙方自定義的聲明
{
"sub": "1234567890",
"name": "XJJ",
"user_id": 1
}
3.signature 使用header中指定的算法將編碼后的header、編碼后的payload肠虽、一個(gè)secret進(jìn)行加密
例如使用的是HMAC SHA256算法幔戏,大致流程類(lèi)似于: HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
這個(gè)signature字段被用來(lái)確認(rèn)JWT信息的發(fā)送者是誰(shuí),并保證信息沒(méi)有被修改
如何使用JWT
在身份鑒定的實(shí)現(xiàn)中税课,傳統(tǒng)方法是在服務(wù)端存儲(chǔ)一個(gè)session闲延,給客戶(hù)端返回一個(gè)cookie,而使用JWT之后韩玩,當(dāng)用戶(hù)使用它的認(rèn)證信息登陸系統(tǒng)之后垒玲,會(huì)返回給用戶(hù)一個(gè)JWT,用戶(hù)只需要本地保存該token(通常使用local storage啸如,也可以使用cookie)即可侍匙。
當(dāng)用戶(hù)希望訪問(wèn)一個(gè)受保護(hù)的路由或者資源的時(shí)候,通常應(yīng)該在Authorization頭部使用Bearer模式添加JWT叮雳,其內(nèi)容看起來(lái)是下面這樣:Authorization: Bearer <token>
因?yàn)橛脩?hù)的狀態(tài)在服務(wù)端的內(nèi)存中是不存儲(chǔ)的想暗,所以這是一種無(wú)狀態(tài)的認(rèn)證機(jī)制。服務(wù)端的保護(hù)路由將會(huì)檢查請(qǐng)求頭Authorization中的JWT信息帘不,如果合法说莫,則允許用戶(hù)的行為。由于JWT是自包含的寞焙,因此減少了需要查詢(xún)數(shù)據(jù)庫(kù)的需要储狭。
JWT的這些特性使得我們可以完全依賴(lài)其無(wú)狀態(tài)的特性提供數(shù)據(jù)API服務(wù),甚至是創(chuàng)建一個(gè)下載流服務(wù)捣郊。因?yàn)镴WT并不使用Cookie的辽狈,所以你可以使用任何域名提供你的API服務(wù)而不需要擔(dān)心跨域資源共享問(wèn)題(CORS)。
JWT類(lèi)庫(kù)
namespace App\Utility;
class JwtBase {
//頭部
private static $header=array(
'alg'=>'HS256', //生成signature的算法
'typ'=>'JWT' //類(lèi)型
);
//使用HMAC生成信息摘要時(shí)所使用的密鑰
private static $key='KEY';
/**
* 獲取jwt token
* @param array $payload jwt載荷 格式如下非必須
* [
* 'iss'=>'jwt_admin', //該JWT的簽發(fā)者
* 'iat'=>time(), //簽發(fā)時(shí)間
* 'exp'=>time()+7200, //過(guò)期時(shí)間
* 'nbf'=>time()+60, //該時(shí)間之前不接收處理該Token
* 'sub'=>'www.admin.com', //面向的用戶(hù)
* 'jti'=>md5(uniqid('JWT').time()) //該Token唯一標(biāo)識(shí)
* ]
* @return bool|string
*/
public static function getToken(array $payload)
{
$arr = [
'iss'=>'yamecent', //該JWT的簽發(fā)者
'iat'=>time(), //簽發(fā)時(shí)間
'exp'=>time()+3600*24*15, //過(guò)期時(shí)間
'nbf'=>time(), //該時(shí)間之前不接收處理該Token
'sub'=>'', //面向的用戶(hù)
'jti'=>md5(uniqid('JWT').time()) //該Token唯一標(biāo)識(shí)
];
$payload = array_merge($arr,$payload);
if(is_array($payload))
{
$base64header=self::base64UrlEncode(json_encode(self::$header,JSON_UNESCAPED_UNICODE));
$base64payload=self::base64UrlEncode(json_encode($payload,JSON_UNESCAPED_UNICODE));
$token=$base64header.'.'.$base64payload.'.'.self::signature($base64header.'.'.$base64payload,self::$key,self::$header['alg']);
return $token;
}else{
return false;
}
}
/**
* 驗(yàn)證token是否有效,默認(rèn)驗(yàn)證exp,nbf,iat時(shí)間
* @param string $Token 需要驗(yàn)證的token
* @return bool|string
*/
public static function verifyToken(string $Token)
{
$tokens = explode('.', $Token);
if (count($tokens) != 3)
return false;
list($base64header, $base64payload, $sign) = $tokens;
//獲取jwt算法
$base64decodeheader = json_decode(self::base64UrlDecode($base64header), JSON_OBJECT_AS_ARRAY);
if (empty($base64decodeheader['alg']))
return false;
//簽名驗(yàn)證
if (self::signature($base64header . '.' . $base64payload, self::$key, $base64decodeheader['alg']) !== $sign)
return false;
$payload = json_decode(self::base64UrlDecode($base64payload), JSON_OBJECT_AS_ARRAY);
//簽發(fā)時(shí)間大于當(dāng)前服務(wù)器時(shí)間驗(yàn)證失敗
if (isset($payload['iat']) && $payload['iat'] > time())
return false;
//過(guò)期時(shí)間小宇當(dāng)前服務(wù)器時(shí)間驗(yàn)證失敗
if (isset($payload['exp']) && $payload['exp'] < time())
return false;
//該nbf時(shí)間之前不接收處理該Token
if (isset($payload['nbf']) && $payload['nbf'] > time())
return false;
return $payload;
}
/**
* base64UrlEncode https://jwt.io/ 中base64UrlEncode編碼實(shí)現(xiàn)
* @param string $input 需要編碼的字符串
* @return string
*/
private static function base64UrlEncode(string $input)
{
return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
}
/**
* base64UrlEncode https://jwt.io/ 中base64UrlEncode解碼實(shí)現(xiàn)
* @param string $input 需要解碼的字符串
* @return bool|string
*/
private static function base64UrlDecode(string $input)
{
$remainder = strlen($input) % 4;
if ($remainder) {
$addlen = 4 - $remainder;
$input .= str_repeat('=', $addlen);
}
return base64_decode(strtr($input, '-_', '+/'));
}
/**
* HMACSHA256簽名 https://jwt.io/ 中HMACSHA256簽名實(shí)現(xiàn)
* @param string $input 為base64UrlEncode(header).".".base64UrlEncode(payload)
* @param string $key
* @param string $alg 算法方式
* @return mixed
*/
private static function signature(string $input, string $key, string $alg = 'HS256')
{
$alg_config=array(
'HS256'=>'sha256'
);
return self::base64UrlEncode(hash_hmac($alg_config[$alg], $input, $key,true));
}
}
使用
$token = JwtBase::getToken(['user_id'=>$user->id]);//生成token
$header = $request->header('Authorization');//驗(yàn)證
$token = explode(' ',$header);
if(!$header || !$token){
return $this->json(419,'登錄信息已過(guò)期呛牲,重新登錄');
}
$data = JwtBase::verifyToken($token[1]);
if(!$data){
return $this->json(419,'登錄信息已過(guò)期刮萌,重新登錄');
}