python中的JWT

为了解决Http无状态问题,产生了cookie和session技术。

 传统的session技术解决了无状态问题,但是不能防止客户端的篡改,而且对于服务端要保存session,在数据量较大,业务规模增加时,还要解决多台服务器之间的session共享问题,使用redis或者memcached等方案。

产生可JWT技术可以解决用户篡改问题,服务器端产生的标识,使用算法加密,对标识签名。

再次收到服务器端的标识,就要检查签名。

这种方法在加密解密时,会消耗cpu计算资源,而且无法让浏览器自己检查过期数据,并且删除。

下边我将尝试解释一下jwt防篡改的原理。

JWT 全称(Json WEB Token)是一种采用Json方式安装传输信息的方式。

python下的实现的pyjwt

文档 : https://pyjwt.readthedocs.io/en/latest/

安装 : 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'xa7gAxb2Lxacxb6lNx94x85xebx04*0xbbxa0xcfqdx1bFxb6LOxIWxb8x8ccxef'

可以看到,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,服务器校验签名,通过就允许访问,在单点登陆时广泛应用。

原文地址:https://www.cnblogs.com/rprp789/p/10017781.html