JWT
JSON Web Tokens,是一種開發的行業標準 RFC 7519 ,用於安全的表示雙方之間的聲明。目前,jwt廣泛應用在系統的用戶認證方面,特別是現在前後端分離項目。
JWT認證流程
在項目開發中,一般會按照上圖所示的過程進行認證,即:用戶登錄成功之後,服務端給用戶瀏覽器返回一個token,以後用戶瀏覽器要攜帶token再去向服務端發送請求,服務端校驗token的合法性,合法則給用戶看數據,否則,返回一些錯誤信息。
傳統token方式和jwt在認證方面的差異化?
-
傳統token方式
用戶登錄成功後,服務端生成一個隨機token給用戶,並且在服務端(數據庫或緩存)中保存一份token,以後用戶再來訪問時需攜帶token,服務端接收到token之後,去數據庫或緩存中進行校驗token的是否超時、是否合法。
-
jwt方式
用戶登錄成功後,服務端通過jwt生成一個隨機token給用戶(服務端無需保留token),以後用戶再來訪問時需攜帶token,服務端接收到token之後,通過jwt對token進行校驗是否超時、是否合法。
JWT創建token
原理
jwt的生成
token
格式如下,即:由.
連接的三段字符串組成。
例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
生成規則如下:
第一段 - HEADER部分:固定包含算法和token類型,對此json進行base64url加密,得到token的第一段
{
"alg": "HS256",
"typ": "JWT"
}
第二段 - PAYLOAD部分:,包含一些數據,對此json進行base64url加密,得到token的第二段。
# 非敏感信息
{
"sub": "1",# 用戶的唯一標識
"name": "hui", # 用戶名
"iat": 1516239022, # 可以自定義
"exp": datetime.datetime(2019, 12, 9, 12, 10, 10, 414935) # token過期時間
}
第三段 - SIGNATURE部分:把前兩段的base密文通過.拼接起來,然後對其進行HS256加密,再然後對hs256密文進行base64url加密,最終得到token的第三段。
base64url(
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
your-256-bit-secret (祕鑰加鹽)
)
)
最後將三段字符串通過 .拼接起來就生成了jwt的token。
注意:
- base64url加密是先做base64加密,然後再將 - 替代 + 及 _ 替代 /
- base64url加密可以反解密。
代碼實現
基於Python的pyjwt模塊
# 安裝 pip install pyjwt
import jwt
import datetime
from jwt import exceptions
SALT = 'iv%x6xo7l7_u9bf_u!9#g#m*)*=ej@bek5)(@u3kh*72+unjv='
def generate_token():
# 構造header
headers = {
'typ': 'jwt',
'alg': 'HS256'
}
# 構造payload
payload = {
'user_id': 1, # 自定義用戶ID
'username': 'hui', # 自定義用戶名
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=1) # 超時時間
}
result = jwt.encode(payload=payload, key=SALT, algorithm="HS256", headers=headers).decode('utf-8')
return result
if __name__ == '__main__':
token = generate_token()
print(token)
JWT校驗token
一般在認證成功後,把jwt生成的token返回給用戶,以後用戶再次訪問時候需要攜帶token,此時jwt需要對token進行超時及合法性校驗。
獲取token之後,會按照以下步驟進行校驗:
-
將token分割成 header_segment、payload_segment、crypto_segment 三部分
jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6 IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_ adQssw5c" signing_input, crypto_segment = jwt_token.rsplit(b'.', 1) header_segment, payload_segment = signing_input.split(b'.', 1)
-
對第一部分header_segment進行base64url解密,得到header
-
對第二部分payload_segment進行base64url解密,得到payload
-
對第三部分crypto_segment進行base64url解密,得到signature
-
對第三部分signature部分數據進行合法性校驗
1、拼接前兩段密文,即:signing_input
2、從第一段明文中獲取加密算法,默認:HS256
3、使用 算法+鹽 對signing_input 進行加密,將得到的結果和signature密文進行比較。
# pip install pyjwt
import jwt
import datetime
from jwt import exceptions
def decode_token(token):
"""
根據token獲取payload
:param token:
:return:
"""
try:
# 從token中獲取payload【不校驗合法性】
# unverified_payload = jwt.decode(token, None, False)
# print(unverified_payload)
# 從token中獲取payload【校驗合法性】
verified_payload = jwt.decode(token, SALT, True)
return verified_payload
except exceptions.ExpiredSignatureError:
print('token已失效')
except jwt.DecodeError:
print('token認證失敗')
except jwt.InvalidTokenError:
print('非法的token')
if __name__ == '__main__':
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU"
payload = decode_token(token)
參考博客:https://www.cnblogs.com/wupeiqi/p/11854573.html