-
jwt概述
-
什么是jwt?
json web token
-
jwt應用場景?
- 微信小程序
- 移動設備
- 前后端分離項目
-
認證方式
-
傳統認證
用戶登錄成功后,服務端下發token,並保存在服務端(database/session/redis/file)中,當客戶端再訪問服務端需要攜帶token,服務端獲取客戶端傳來的token后再去服務端(database/session/redis/file)中獲取token進行時間和token校驗,如果一樣,則順利訪問,否則提示token不合法或者過期。
-
jwt認證
用戶登錄成功后,服務端下發token,此時服務端並不保存token,當客戶端再訪問服務端時,需要攜帶token,服務端獲取客戶端傳來的token后,對此token通過某些算法進行分析來確認token是否合法。
-
jwt實現過程
-
組成部分
-
頭部(HEADER)
{ "alg": "HS256", "typ": "JWT" }
-
載荷(PAYLOAD)
{ "sub": "1234567890", "name": "John Doe", "exp": 1516239022 }
-
簽名(VERIFY SIGNATURE)
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), SALT )
-
-
如何組成
- 將頭部使用base64UrlEncode編碼 得到StringA
- 將載荷使用base64UrlEncode編碼 得到StringB
- 將第一步和第二步 使用
.
進行字符串拼接 得到StringA.StringB - 設置鹽值(SALT)
- 將第三步的字符串和鹽值進行hash256加密(為了安全使用鹽)得到hash256('StringA.StringB',SALT)
- 將第3步得到的字符串和第五步得到的hash256加密后的字符串用
.
進行拼接
-
最終生成結構
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
-
輔助函數
# 封裝 base64UrlEncode方法 def base64UrlEncode(data): ''' @params : dict @return : string ''' # 1. 判斷參數是否為字典,如不是字典直接返回 if type(data) is not dict: return json.dumps({"code":1001,"msg":"參數類型不合法"},ensure_ascii=False) # 2.將字典轉成json串 json_str = json.dumps(data) # 3.將json串轉成字節 bytes_str = json_str.encode('utf-8') # 4.base64編碼 b64_bytes = base64.b64encode(bytes_str) # 5.將字節類型轉成字符串類型,並返回 return b64_bytes.decode('utf-8')
# 封裝sha256方法 import hashlib def sha256hex(raw,salt): ''' @params: raw 原始字符串 @params: salt 隨機鹽值 @return : string ''' new_str = raw+salt sha256 = hashlib.sha256() sha256.update(new_str.encode()) return sha256.hexdigest()
-
生成token
# 代碼塊(生成token) from utils.helper import base64UrlEncode,sha256hex from rest_framework.views import APIView from rest_framework.response import Response from django.conf import settings class CreateTokenView(APIView): def post(self,request): # jwt頭部 header = { "alg": "HS256", "typ": "JWT" } # jwt載荷 payload = { "sub": "1234567890", "name": "John Doe", "exp": 1516239022 } # 頭部base64編碼 header_string = base64UrlEncode(header) # 載荷base64編碼 payload_string = base64UrlEncode(payload) # 鹽值 salt = settings.SECRET_KEY # 生成簽名 sign = sha256hex(header_string+'.'+payload_string,salt) # 構造token token = header_string+"."+payload_string+"."+sign # 返回 return Response({ "code":20000, "data":{ "token":token } })
# 返回結果 { "code": 20000, "data": { "token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogIkpvaG4gRG9lIiwgImV4cCI6IDE1MTYyMzkwMjJ9.c7364fb2a00fe6aa843fae23e179fe97dd820f26a47a769c621ff560945278c7" } }
-
驗證token
# 代碼塊(生成token) from utils.helper import base64UrlEncode,sha256hex from rest_framework.views import APIView from rest_framework.response import Response from django.conf import settings class CheckTokenView(APIView): def post(self,request): # 獲取http headers token token = request.META.get("HTTP_TOKEN") # 將token分解三部分 payload_b64_header = token.split('.')[0] payload_b64_payload = token.split('.')[1] payload_b64_sign = token.split('.')[2] # 將第二部分解析,獲取過期時間 json_str = base64_decode(payload_b64_payload) exp = json.loads(json_str) # 判斷當前時間和過期時間 if exp['exp'] < int(time.time()): return Response({ "code":1003, "msg":"token過期" }) # 從配置文件中讀取鹽值 salt = settings.SECRET_KEY # 在有效期內,判斷簽名是否一致 sign = sha256hex(payload_b64_header+'.'+payload_b64_payload,salt) if sign == payload_b64_sign: return Response({ "code":1000, "msg":"驗證成功" }) return Response({ "code":"1002", "msg":"驗證失敗" })
-