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