天天看點

JWT---自我實作什麼是: json-web-token

什麼是: json-web-token

JSON Web Token(JWT)是一個非常輕巧的規範。這個規範允許我們使用JWT在使用者和伺服器之間傳遞安全可靠的資訊。

一個JWT實際上就是一個字元串,它由三部分組成,頭部、載荷與簽名。

為什麼使用JWT?

1.jwt本質上也是為了解決http無狀态。

2.随着技術的發展,分布式web應用的普及,通過session管理使用者登入狀态成本越來越高,是以慢慢發展成為token的方式做登入身份校驗,然後通過token去取redis中的緩存的使用者資訊,随着之後jwt的出現,校驗方式更加簡單便捷化,無需通過redis緩存,而是直接根據token取出儲存的使用者資訊,以及對token可用性校驗,單點登入更為簡單。

3.某些特定情況下我們無法使用cookie和session存儲資料,例如(安卓用戶端和ios用戶端),這時候就需要使用jwt-token。

頭部(Header)

頭部用于描述關于該JWT的最基本的資訊,例如其類型以及簽名所用的算法等。這也可以被表示成一個JSON對象。

{“typ”:“JWT”,“alg”:“HS256”}

在頭部指明了簽名算法是HS256算法。 我們進行Base64編碼,編碼後的字元串如下:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
           

載荷(playload)

​載荷就是存放有效資訊的地方,格式為字典-此部分分為公有聲明和私有聲明

公共聲明:JWT提供了内置關鍵字用于描述常見的問題
           

此部分均為可選項,使用者根據自己需求 按需添加key,常見公共聲明如下:

{'exp':xxx, # Expiration Time 此token的過期時間的時間戳
 'iss':xxx,# (Issuer) Claim 指明此token的簽發者
 'aud':xxx, #(Audience) Claim 指明此token的簽發面向群體
 'iat':xxx, # (Issued At) Claim 指明此建立時間的時間戳
}
           
私有聲明:使用者可根據自己業務需求,添加自定義的key,例如如下:
           
公共聲明和私有聲明均在同一個字典中;轉成json串并用base64加密
           

簽證(signature)

jwt的第三部分是一個簽證資訊,這個簽證資訊由三部分組成:

​ 簽名規則如下:

​ 根據header中的alg确定 具體算法,以下用 HS256為例

​ HS256(自定義的key , base64後的header + ‘.’ + base64後的payload)

​ 解釋:用自定義的key, 對base64後的header + ‘.’ + base64後的payload進行hmac計算

jwt結果格式

base64(header) + ‘.’ + base64(payload) + ‘.’ + base64(sign)

自定義jwt

import time
import base64
import hmac
import json
import copy

class Jwt():

    def __init__(self):
        pass

    @staticmethod
    def encode(payload,key,exp=300):
        '''
        :param payload: 荷載内容
        :param key: 自己的鑰匙(加鹽)
        :param exp: 過期時間
        :return: 生成的token
        '''
        header = {'alg':'HS256','typ':'JWT'}
        # 将字典轉為json串
        header_json = json.dumps(header,sort_keys=True,separators=(',',':'))
        # 将json串進行base64加密
        str_header = Jwt.b64_encode(header_json.encode())

        exp = time.time() + exp
        payload = copy.deepcopy(payload)
        payload['exp'] = exp
        payload = json.dumps(payload,sort_keys=True,separators=(',',':'))
        str_payload = Jwt.b64_encode(payload.encode())

        str_b64 = str_header + b'.' + str_payload
        hm_sign = hmac.new(key.encode(),str_b64,digestmod='SHA256')
        str_sign = Jwt.b64_encode(hm_sign.digest())
        return str_header+b'.'+str_payload+b'.'+str_sign


    @staticmethod
    def b64_encode(j_s):
	    #将 '+'替換成 '-',将'/'替換成'_',并且将多餘的'='去掉
        return base64.urlsafe_b64encode(j_s).replace(b'=',b'')

    @staticmethod
    def b64_decode(b_s):
    	# 将缺少的'='補全
        rem = len(b_s) % 4
        if rem > 0:
            b_s += b'=' * (4-rem)
        return base64.urlsafe_b64decode(b_s)

    @staticmethod
    def decode(token,key):
        header_bs,payload_bs,sign = token.split(b'.')
        hm = hmac.new(key.encode(),header_bs+b'.'+payload_bs,digestmod='SHA256')
        if sign != Jwt.b64_encode(hm.digest()):
            raise ('校驗失敗')

        payload_js = Jwt.b64_decode(payload_bs)
        payload = json.loads(payload_js)
        if payload['exp'] < time.time():
            raise ('時間已過期')
        return payload


    def run(self):
        payload = {'username':'caoxudong'}
        key = 'cxd'
        # 生成token
        token = self.encode(payload,key)
        print(token)
        # 驗證通過後傳回字典内容
        value = self.decode(token, key)
        print(value)

if __name__ == '__main__':
    s = Jwt()
    s.run()

           

輸出結果:

b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzUyOTE5NTYuMzI2Mjg1NCwidXNlcm5hbWUiOiJjYW94dWRvbmcifQ.wimiGX6MbBHwGAm0oZWyjksUNOBbEjyhdhCuy_S1-ls'

{'exp': 1575291956.3262854, 'username': 'caoxudong'}