JWT

什么是JWT ?

Json web token (JWT), 是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標準((RFC 7519).定義了一種簡潔的,自包含的方法用于通信雙方之間以JSON對象的形式安全的傳遞信息。因為數(shù)字簽名的存在,這些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘鑰對進行簽名。

JWT請求流程

image.png

1.用戶使用賬號和面發(fā)出post請求;
2.服務(wù)器使用私鑰創(chuàng)建一個jwt;
3.服務(wù)器返回這個jwt給瀏覽器;
4.瀏覽器將該jwt串在請求頭中像服務(wù)器發(fā)送請求;
5.服務(wù)器驗證該jwt;
6.返回響應(yīng)的資源給瀏覽器。

JWT的主要應(yīng)用場景

身份認證在這種場景下,一旦用戶完成了登陸,在接下來的每個請求中包含JWT,可以用來驗證用戶身份以及對路由,服務(wù)和資源的訪問權(quán)限進行驗證。由于它的開銷非常小,可以輕松的在不同域名的系統(tǒng)中傳遞,所有目前在單點登錄(SSO)中比較廣泛的使用了該技術(shù)。

信息交換在通信的雙方之間使用JWT對數(shù)據(jù)進行編碼是一種非常安全的方式,由于它的信息是經(jīng)過簽名的,可以確保發(fā)送者發(fā)送的信息是沒有經(jīng)過偽造的。

優(yōu)點:

1.簡潔(Compact): 可以通過URL,POST參數(shù)或者在HTTP header發(fā)送,因為數(shù)據(jù)量小,傳輸速度也很快
2.自包含(Self-contained):負載中包含了所有用戶所需要的信息,避免了多次查詢數(shù)據(jù)庫
3.因為Token是以JSON加密的形式保存在客戶端的,所以JWT是跨語言的,原則上任何web形式都支持。
不需要在服務(wù)端保存會話信息。

JWT的結(jié)構(gòu)

JWT是由三段信息構(gòu)成的,將這三段信息文本用.連接一起就構(gòu)成了JWT字符串。
如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

JWT包含了三部分:

1.Header 頭部(標題包含了令牌的元數(shù)據(jù),并且包含簽名和/或加密算法的類型)
2.Payload 負載 (類似于飛機上承載的物品)
3.Signature 簽名/簽證

Header

JWT的頭部承載兩部分信息:token類型和采用的加密算法。

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

聲明類型:這里是jwt

聲明加密的算法:通常直接使用 HMAC SHA256

加密算法是單向函數(shù)散列算法,常見的有MD5、SHA、HAMC。

MD5(message-digest algorithm 5) (信息-摘要算法)縮寫,廣泛用于加密和解密技術(shù),常用于文件校驗。校驗?不管文件多大,經(jīng)過MD5后都能生成唯一的MD5值

SHA (Secure Hash Algorithm,安全散列算法),數(shù)字簽名等密碼學(xué)應(yīng)用中重要的工具,安全性高于MD5

HMAC (Hash Message Authentication Code),散列消息鑒別碼,基于密鑰的Hash算法的認證協(xié)議。用公開函數(shù)和密鑰產(chǎn)生一個固定長度的值作為認證標識,用這個標識鑒別消息的完整性。常用于接口簽名驗證.

Payload

載荷就是存放有效信息的地方。
有效信息包含三個部分

1.標準中注冊的聲明
2.公共的聲明
3.私有的聲明

標準中注冊的聲明 (建議但不強制使用) :

iss: jwt簽發(fā)者
sub: 面向的用戶(jwt所面向的用戶)
aud: 接收jwt的一方
exp: 過期時間戳(jwt的過期時間,這個過期時間必須要大于簽發(fā)時間)
nbf: 定義在什么時間之前,該jwt都是不可用的.
iat: jwt的簽發(fā)時間
jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。

公共的聲明:

公共的聲明可以添加任何的信息,一般添加用戶的相關(guān)信息或其他業(yè)務(wù)需要的必要信息.但不建議添加敏感信息,因為該部分在客戶端可解密.

私有的聲明:

私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感信息,因為base64是對稱解密的,意味著該部分信息可以歸類為明文信息。

Signature

jwt的第三部分是一個簽證信息
這個部分需要base64加密后的header和base64加密后的payload使用.連接組成的字符串,然后通過header中聲明的加密方式進行加鹽secret組合加密,然后就構(gòu)成了jwt的第三部分。

密鑰secret是保存在服務(wù)端的,服務(wù)端會根據(jù)這個密鑰進行生成token和進行驗證,所以需要保護好。

代碼如下:


class Jwt {

    //頭部
    private static $header=array(
        'alg'=>'HS256', //生成signature的算法
        'typ'=>'JWT'  //類型
    );

    //使用HMAC生成信息摘要時所使用的密鑰
    private static $key='123456';


    /**
     * 獲取jwt token
     * @param array $payload jwt載荷  格式如下非必須
     * [
     * 'iss'=>'jwt_admin', //該JWT的簽發(fā)者
     * 'iat'=>time(), //簽發(fā)時間
     * 'exp'=>time()+7200, //過期時間
     * 'nbf'=>time()+60, //該時間之前不接收處理該Token
     * 'sub'=>'www.admin.com', //面向的用戶
     * 'jti'=>md5(uniqid('JWT').time()) //該Token唯一標識
     * ]
     * @return bool|string
     */
    public static function getToken(array $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;
        }
    }


    /**
     * 驗證token是否有效,默認驗證exp,nbf,iat時間
     * @param string $Token 需要驗證的token
     * @return bool|string
     */
    public static function verifyToken($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;

        //簽名驗證
        if (self::signature($base64header . '.' . $base64payload, self::$key, $base64decodeheader['alg']) !== $sign)
            return false;

        $payload = json_decode(self::base64UrlDecode($base64payload), JSON_OBJECT_AS_ARRAY);

        //簽發(fā)時間大于當(dāng)前服務(wù)器時間驗證失敗
        if (isset($payload['iat']) && $payload['iat'] > time())
            return false;

        //過期時間小宇當(dāng)前服務(wù)器時間驗證失敗
        if (isset($payload['exp']) && $payload['exp'] < time())
            return false;

        //該nbf時間之前不接收處理該Token
        if (isset($payload['nbf']) && $payload['nbf'] > time())
            return false;

        return $payload;
    }




    /**
     * base64UrlEncode  https://jwt.io/ 中base64UrlEncode編碼實現(xiàn)
     * @param string $input 需要編碼的字符串
     * @return string
     */
    private static function base64UrlEncode($input)
    {
        return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
    }

    /**
     * base64UrlEncode https://jwt.io/ 中base64UrlEncode解碼實現(xiàn)
     * @param string $input 需要解碼的字符串
     * @return bool|string
     */
    private static function base64UrlDecode($input)
    {
        $remainder = strlen($input) % 4;
        if ($remainder) {
            $addlen = 4 - $remainder;
            $input .= str_repeat('=', $addlen);
        }
        return base64_decode(strtr($input, '-_', '+/'));
    }

    /**
     * HMACSHA256簽名  https://jwt.io/ 中HMACSHA256簽名實現(xiàn)
     * @param string $input 為base64UrlEncode(header).".".base64UrlEncode(payload)
     * @param string $key
     * @param string $alg  算法方式
     * @return mixed
     */
    private static function signature($input,  $key,  $alg = 'HS256')
    {
        $alg_config=array(
            'HS256'=>'sha256'
        );
        return self::base64UrlEncode(hash_hmac($alg_config[$alg], $input, $key,true));
    }
}

使用示示例

$payload_test=array('iss'=>'admin','iat'=>time(),'exp'=>time()+7200,'nbf'=>time(),'sub'=>'www.admin.com','jti'=>md5(uniqid('JWT').time()));;
$token_test=Jwt::getToken($payload_test);
echo "<pre>";
echo $token_test;


$getPayload_test=Jwt::verifyToken($token_test);
echo "<br><br>";
var_dump($getPayload_test);
echo "<br><br>";

token 存放

通常應(yīng)該在請求的header頭中的 Authorization字段使用 Bearer模式添加JWT(Authorization: Bearer )
服務(wù)器端 $_SERVER[HTTP_AUTHORIZATION]: 獲取

使用方式 根據(jù)自己的應(yīng)用場景自行調(diào)節(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容