python中的JWT

爲了解決Http無狀態問題,產生了cookie和session技術。python

 傳統的session技術解決了無狀態問題,可是不能防止客戶端的篡改,並且對於服務端要保存session,在數據量較大,業務規模增長時,還要解決多臺服務器之間的session共享問題,使用redis或者memcached等方案。redis

產生可JWT技術能夠解決用戶篡改問題,服務器端產生的標識,使用算法加密,對標識簽名。算法

再次收到服務器端的標識,就要檢查簽名。瀏覽器

這種方法在加密解密時,會消耗cpu計算資源,並且沒法讓瀏覽器本身檢查過時數據,而且刪除。安全

下邊我將嘗試解釋一下jwt防篡改的原理。服務器

JWT 全稱(Json WEB Token)是一種採用Json方式安裝傳輸信息的方式。cookie

python下的實現的pyjwtsession

文檔 : https://pyjwt.readthedocs.io/en/latest/memcached

安裝 : pip install pyjwt測試

先看如下代碼:

import jwt

key = "secret"
token = jwt.encode({"test": "100"},key,"HS256")
print(token)
header,payload,signature = token.split(b".")
print(header)
print(payload)
print(signature)

 

顯示

b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0ZXN0IjoiMTAwIn0.p2dBskystmxOlIXrBCowu6DPcWQbRrZMT3hJV7iMY-8'
b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9'
b'eyJ0ZXN0IjoiMTAwIn0'
b'p2dBskystmxOlIXrBCowu6DPcWQbRrZMT3hJV7iMY-8'

能夠知道JWT由3段由點分割的部分組成。

它使用的編碼是base64編碼,咱們導入如下

import jwt

key = "secret"
token = jwt.encode({"test": "100"},key,"HS256")
print(token)
header,payload,signature = token.split(b".")

import base64

def equal(b: bytes):
    """用來補齊被JWT去掉的等號"""
    rest = len(b) % 4
    return b + b'=' * rest

print("header=",base64.urlsafe_b64decode(equal(header)))
print("payout=", base64.urlsafe_b64decode(equal(payload)))
print("signature", base64.urlsafe_b64decode(equal(signature)))

 

顯示:

header= b'{"typ":"JWT","alg":"HS256"}'
payout= b'{"test":"100"}'
signature b'\xa7gA\xb2L\xac\xb6lN\x94\x85\xeb\x04*0\xbb\xa0\xcfqd\x1bF\xb6LOxIW\xb8\x8cc\xef'

能夠看到,JWT中傳輸的token內容由三部分組成

  • header 是由數據類型和加密算法組成
  • payload 是傳輸的數據部分,
  • signature 部分就時簽名,是由前兩部分和加密算法實現的,看如下代碼

下邊是源碼中的一部分,signing_input就是將前兩部分轉爲JSON格式,再使用base64編碼,鏈接起來,傳入下邊的方法中,alg_obj是方法,key是預處理key

signing_input = b'.'.join(segments)
        try:
            alg_obj = self._algorithms[algorithm]
            key = alg_obj.prepare_key(key)
            signature = alg_obj.sign(signing_input, key)

咱們模仿一下,可得

import jwt

# Create your tests here.

key = "secret"
token = jwt.encode({"test": "100"},key,"HS256")
header,payload,signature = token.split(b".")
print(signature)

import base64

# def equal(b: bytes):
#     """用來補齊被JWT去掉的等號"""
#     rest = len(b) % 4
#     return b + b'=' * rest

# print("header=",base64.urlsafe_b64decode(equal(header)))
# print("payout=", base64.urlsafe_b64decode(equal(payload)))
# print("signature", base64.urlsafe_b64decode(equal(signature)))

from jwt import algorithms
alg = algorithms.get_default_algorithms()["HS256"]
newkey = alg.prepare_key(key)
signature_input = b".".join([header,payload])
signature = alg.sign(signature_input,newkey)

print(base64.urlsafe_b64encode(signature))

 

顯示,能夠看到

b'p2dBskystmxOlIXrBCowu6DPcWQbRrZMT3hJV7iMY-8'
b'p2dBskystmxOlIXrBCowu6DPcWQbRrZMT3hJV7iMY-8='

因此在密碼相同時,傳輸相同的信息,獲得的值是同樣的。

密碼要足夠強,且要足夠安全。能夠在測試下改變內容,看簽名會不會改變。

import jwt

# Create your tests here.

key = "secret"
token = jwt.encode({"test": "100"},key,"HS256")
header,payload,signature = token.split(b".")
print(signature)

import base64

# def equal(b: bytes):
#     """用來補齊被JWT去掉的等號"""
#     rest = len(b) % 4
#     return b + b'=' * rest

# print("header=",base64.urlsafe_b64decode(equal(header)))
# print("payout=", base64.urlsafe_b64decode(equal(payload)))
# print("signature", base64.urlsafe_b64decode(equal(signature)))
token = jwt.encode({"test": "1000"},key,"HS256") # 改變payload內容
header,payload,signature = token.split(b".")

from jwt import algorithms
alg = algorithms.get_default_algorithms()["HS256"]
newkey = alg.prepare_key(key)
signature_input = b".".join([header,payload])
signature = alg.sign(signature_input,newkey)

print(base64.urlsafe_b64encode(signature))

能夠看到簽名變了

b'p2dBskystmxOlIXrBCowu6DPcWQbRrZMT3hJV7iMY-8'
b'uCmPr1eOuLz8IDLQfN8t_DsyrB9PYfdvdueeHSBWN04='

 

通過以上測試,能夠知道,數據在使用JWT傳輸時,是明文傳輸的,注意不要傳輸敏感數據。

JWT的防篡改還可使用公鑰,私鑰,來防止用戶被劫持的可能性。

在用戶登錄成功後可使用JWT技術,登陸後給用戶JWT,用戶發請求時加上jwt,服務器校驗簽名,經過就容許訪問,在單點登錄時普遍應用。

相關文章
相關標籤/搜索