什么是JWT

JWT

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

PyJWT

是Python对JWT的实现

相关文档

安装

pip install pyjwt

使用简介

  • jwt.encode(payload: Dict[str, Any], key: str, algorithm: str = "HS256", headers: Optional[Dict] = None, json_encoder: Optional[Type[json.JSONEncoder]] = None, ) -> str
import jwt

key = "thisisthekey"
payload = {'name': 'monkeyjerry'}
token = jwt.encode(payload, key, algorithm="HS256")  # 默认算法HS256

print(token, type(token))
--------输出结果------------
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoibW9ua2V5amVycnkifQ.THZNd4mPF_HPU4xXOgymKejNT1yP_lSox_xWFODPBdY <class 'str'>

jwt生成的token分为三部分:

  • header:由数据类型、加密算法构成

  • payload:负载就是要传输的数据,一般来说放入python对象即可,会被json序列化

  • signature:签名部分。是前面2部分数据分别base64编码后使用点号连接后,加密算法使用key计算好一个结果,再被base64编码,得到签名

  • jwt.decode(jwt: str, key: str = "", algorithms: List[str] = None, options: Dict = None, **kwargs, ) -> Dict[str, Any])

    • 注:algorithms必须为list
import jwt

key = "thisisthekey"
payload = {'name': 'monkeyjerry'}
token = jwt.encode(payload, key, algorithm="HS256")

token_decrypt = jwt.decode(token, key, algorithms=["HS256"])
print(token_decrypt)
-------输出结果-------
{'name': 'monkeyjerry'}

数据签名

import base64
import jwt

key = "thisisthekey"
payload = {'name': 'monkeyjerry'}
token = jwt.encode(payload, key, algorithm="HS256")
header, payload, signature = token.split('.')

def add_eq(encrypt_str):
    '''为base64编码补齐等号'''
    rest = 4 - len(encrypt_str) % 4
    return encrypt_str + '=' * rest

print('header=', base64.urlsafe_b64decode(add_eq(header)))
print('payload=', base64.urlsafe_b64decode(add_eq(payload)))
print('signature=', base64.urlsafe_b64decode(add_eq(signature)))
  • 所有数据都是明文传输的,只是做了base64,因此敏感信息不要使用jwt
  • 数据签名的目的不是为了隐藏数据,而是保证数据不被篡改
# jwt.encode() 部分源码如下
......
segments.append(base64url_encode(json_header))
segments.append(base64url_encode(payload))

# Segments
signing_input = b".".join(segments)
try:
    alg_obj = self._algorithms[algorithm]
    key = alg_obj.prepare_key(key)  # 1
    signature = alg_obj.sign(signing_input, key)  # 2
......
segments.append(base64url_encode(signature))
encoded_string = b".".join(segments)
return encoded_string.decode("utf-8")
......
  • 数据签名的目的不是为了隐藏数据,而是保证数据不被篡改。如果数据篡改了,发回到服务器端,服务器使用自己的key再计算一遍,然后进行签名比对,一定对不上签名
import base64
import jwt

key = "thisisthekey"
payload = {'name': 'monkeyjerry'}
token = jwt.encode(payload, key, algorithm="HS256")  # 客户端发送的数据(jwt)
_, _, signature_send = token.rpartition('.')  # 发送数据中的签名
print(signature_send)

# 数据签名防篡改:服务端使用自己的key再计算一遍得到签名后与发送的数据签名进行比对
# 仿照jwt.encode关键代码计算即可
from jwt import algorithms

alg = algorithms.get_default_algorithms()['HS256']
new_key = alg.prepare_key(key)
# 获取前两部分 header, payload
signing_input, _, _ = token.rpartition('.')
# print(signing_input)
# 使用key签名
signature = alg.sign(signing_input.encode(), new_key)
# print(signature)
print('-------------------------')
print(base64.urlsafe_b64encode(signature).decode().strip('='))
print('~~~~~~~~~~~~~~~~~~~~~~~~~')

signing_input_modified = signing_input.encode() + b'modified data'
signature = alg.sign(signing_input_modified, new_key)
print(base64.urlsafe_b64encode(signature).decode().strip('='))
---------------- 输出结果----------------------
THZNd4mPF_HPU4xXOgymKejNT1yP_lSox_xWFODPBdY
-------------------------
THZNd4mPF_HPU4xXOgymKejNT1yP_lSox_xWFODPBdY
~~~~~~~~~~~~~~~~~~~~~~~~~
SQhR7tNDi5lkwyCs0QQfhI1RfYF592DnoVTh7vFV9sg

这里的key仅用于示例,实际项目中一般会使用更高强度的key

  • jwt使用场景
    • 认证:这是jwt最常用的场景,一旦用户登录成功,就会得到jwt,然后请求中就可以带上这个Jwt。服务器中jwt验证通过,就可以被允许访问资源。甚至可以在不同域名中传递,在单点登录(Single Sign On)中应用广泛
    • 数据交换:jwt可以防止数据被篡改,它还可以使用公钥、私钥加密,确保请求的发送者是可信的

参考及扩展阅读