JWT(Json WEB Token)

scm1911發表於2019-03-24

JWT 簡介

JWTJson WEB Token)是一種採用 Json 方式安裝傳輸資訊的方式。 JWT 有針對各種開發語言的庫。 python 中使用的是 PyJWT,它是 PythonJWT 的實現。

JWT 應用場景

服務端往往需要一個 ID 來表示客戶端的身份,可以使用 session ,也可以在服務端建立一個 ID 返回給客戶端。但是,要保證客戶端不可篡改。JWT 就可以實現這種需求。

  • JWT 是在服務端生成一個標識,並使用某種演算法對標識簽名。
  • 服務端收到客戶端發來的標識,需要檢查簽名。
  • 這種方案的缺點是,加密、解密需要消耗 CPU 計算資源,無法讓瀏覽器自己主動檢查過期的資料以清除。
  • 這種方案的優點是,不需要在服務端存放大量的session,減少了對記憶體的壓力。

  • 用於認證

    常應用於服務端認證介面的 session 方案 ,這是 Jwt 最常用的場景,一旦使用者登入成功,就會得到 Jwt ,然後請求中就可以帶上這個 Jwt 。伺服器中 Jwt 驗證通過,就可以被允許訪問資源。甚至可以在不同域名中傳遞,在單點登入(Single Sign On)中應用廣泛。

  • 用於資料交換

    Jwt 可以防止資料被篡改,它還可以使用公鑰、私鑰,確保請求的傳送者是可信的

JWT 安裝

pip install pyjwt

JWT 原理

可知 jwt 生成的 token 分為三部分

  1. header: 由資料型別、加密演算法構成
  2. payload: 負載就是要傳輸的資料,一般來說放入python物件即可,會被 json 序列化
  3. signature: 簽名部分。是前面2部分資料分別 base64 編碼後 使用點號連線後,加密演算法使用 key 計算好一個結果,再被 base64 編碼,得到簽名

所有資料都是明文傳輸的,只是做了 base64,如果是敏感資訊,請不要使用 jwt
資料簽名的目的不是為了隱藏資料,而是保證資料不被篡改。如果資料篡改了,發回到伺服器端,伺服器使
用自己的 key 再計算一遍,然後進行簽名比對,一定對不上簽名。

Base64URL

  • HeaderPayload 串型化的演算法是 Base64URL。這個演算法跟 Base64 演算法基本類似,但有一些小的不同。
  • JWT 作為一個令牌(token),有些場合可能會放到 URL(比如 api.example.com/?token=xxx)。Base64 有三個字元 +/=,在 URL 裡面有特殊含義,所以要被替換掉:= 被省略、+ 替換成 -/ 替換成 ***_*** 。這就是 Base64URL 演算法。

JWT 使用

JWT 方法說明

方法 說明
jwt.encode(payload, key, algorithm='HS256', headers=None, json_encoder=None) 生成 jwt, bytes 型別的 base64 編碼
jwt.decode(jwt, key='', verify=True, algorithms=None, options=None, **kwargs) 解碼 jwt 為 bytes 型別的 json
jwt.get_unverified_header(jwt) 返回一個未驗證的 jwt 頭部資訊

jwt.encode 引數說明

    def encode(self,
               payload,  # type: Union[Dict, bytes]
               key,  # type: str
               algorithm='HS256',  # type: str
               headers=None,  # type: Optional[Dict]
               json_encoder=None  # type: Optional[Callable]
               ):

jwt.decode 引數說明

    def decode(self,
               jwt,  # type: str
               key='',   # type: str
               verify=True,  # type: bool
               algorithms=None,  # type: List[str]
               options=None,  # type: Dict
               **kwargs):

jwt.get_unverified_header() 引數說明

    def get_unverified_header(self, jwt):
        """Returns back the JWT header parameters as a dict()

        Note: The signature is not verified so the header parameters
        should not be fully trusted until signature verification is complete
        """
        headers = self._load(jwt)[2]
        self._validate_headers(headers)

        return headers

jwt.encode() 方法使用

import jwt
password = '123456'
# 生成bytes型別的token
token = jwt.encode({"payload":'this is my payload'}, password, 'HS256')

print('    token:',token)
head, payload, signature = token.split(b'.')  # 將成成的token(是bytes型別)切割成三段: head, payload, signature
print('     head:',head)
print('  payload:',payload)
print('signature:',signature)
------------------------------結果--------------------------------------
    token: b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXlsb2FkIjoidGhpcyBpcyBteSBwYXlsb2FkIn0.bPGtksZQ-ncY5mya0bF7EmpO82nlN1ohk9L2Dl2D4RA'
     head: b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9'
  payload: b'eyJwYXlsb2FkIjoidGhpcyBpcyBteSBwYXlsb2FkIn0'
signature: b'bPGtksZQ-ncY5mya0bF7EmpO82nlN1ohk9L2Dl2D4RA'

jwt.decode() 方法使用

import jwt
import base64
password = '123456'
# 生成bytes型別的token
token = jwt.encode({"payload":'this is my payload'}, password, 'HS256')

# 此函式是為了避免轉碼時候 base64 編碼結尾處缺少 b'=',而報錯.
def addpad(x):
    suffix = 4 - len(x) % 4
    return x + b'=' * suffix

head, payload, signature = token.split(b'.')  # 將成成的token(是bytes型別)切割成三段: head, payload, signature

# 將 token (bytes 型別)轉換為原來的型別
token_decode = jwt.decode(token, password, algorithms=['HS256'])
head_decode = base64.urlsafe_b64decode(addpad(head))
payload_decode = base64.urlsafe_b64decode(addpad(payload))
signature_decode = base64.urlsafe_b64decode(addpad(signature))

print('    token_decode:',token_decode, type(token_decode))
print('     head_decode:',head_decode, type(head_decode))
print('  payload_decode:',payload_decode, type(payload_decode))
print('signature_decode:',signature_decode, type(signature_decode))

------------------------------結果--------------------------------------
    token_decode: {'payload': 'this is my payload'} <class 'dict'>
     head_decode: b'{"typ":"JWT","alg":"HS256"}' <class 'bytes'>
  payload_decode: b'{"payload":"this is my payload"}' <class 'bytes'>
signature_decode: b'l\xf1\xad\x92\xc6P\xfaw\x18\xe6l\x9a\xd1\xb1{\x12jN\xf3i\xe57Z!\x93\xd2\xf6\x0e]\x83\xe1\x10' <class 'bytes'>

jwt.decode() 方法實現過期驗證

import jwt
import datetime
import threading

event = threading.Event()

passwd = '123456'

token = jwt.encode({
    'a':'test',
    'exp': int(datetime.datetime.now().timestamp() + 5)
    }, passwd, 'HS256')

print(token)
print('===============================')
print('start at about:     ',int(datetime.datetime.now().timestamp()))
while True:
    if not event.wait(1):
        try:
            print(jwt.decode(token, passwd))
        except Exception as e:
            print(e)
            break

執行結果

b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhIjoidGVzdCIsImV4cCI6MTU1MzQ0MDc5OH0.jzwOxmsp3zqVTXi38hsmu8K7c47j4NV8nPxQe9cQ3E0'
===============================
start at about:      1553440793
{'a': 'test', 'exp': 1553440798}
{'a': 'test', 'exp': 1553440798}
{'a': 'test', 'exp': 1553440798}
{'a': 'test', 'exp': 1553440798}
{'a': 'test', 'exp': 1553440798}
Signature has expired

jwt.get_unverified_header(jwt) 方法使用

import jwt
password = '123456'
token = jwt.encode({"payload":'this is my payload'}, password, 'HS256')

print('     token:',token)
print('jwt_header:',jwt.get_unverified_header(token))
------------------------------結果--------------------------------------
     token: b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXlsb2FkIjoidGhpcyBpcyBteSBwYXlsb2FkIn0.bPGtksZQ-ncY5mya0bF7EmpO82nlN1ohk9L2Dl2D4RA'
jwt_header: {'typ': 'JWT', 'alg': 'HS256'}

相關文章