什麼是: 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'}