JWT 认证 以及Django 中的应用
jwt 认证
私钥、公钥、CA认证
用一套加密规则 加密和解密
RSA加密 (非对称的加密)
摘要算法:MD5 FTP/互联网下载软件校验MD5
私钥 --RSA算法-->公钥
加密解密:只要你有我的公钥你就能知道我发的消息是什么。
数字签名:只要用我的公钥把一个消息解密了,那么这个消息就一定是我发的。
小故事:公钥、私钥、CA认证阅读
组成:
-
村长博客: www.cnblogs.com/leguan1314
-
header.payload.sign
==>>头部.载荷.签名
-
依赖包:
- import hmac
import time
import json
import base64
import hashlib
import jwt
- import hmac
头部:
- 就是一个 字典 包含了 typ: token 类型 alg: 签名算法
headers = {
# 申明token类型 `type`
"typ": "JWT",
# 声明你的签名算法
"alg": "HS256"
}
# 头部被 `base64` 编码
header_string = base64.urlsafe_b64encode(json.dumps(headers).encode("utf-8")).replace(b'=', b'')
# 载荷(用户信息)
载荷:
- 用户信息
payload = {
# TODO 公传参数
# 签发者
"iss": "liuzhichao",
# 什么时候给的会话凭证
"iat": int(time.time()),
# 过期时间(什么时候过期)
"exp": int(time.time()) + 100,
# 接收 `jwt` 的一方
"aud": "luffy",
# TODO 自定义参数
# 用户名
"username": "XXX",
# 用户ID
"user_id": "101"
}
# 载荷被 `base64` 编码
payload_string = base64.urlsafe_b64encode(json.dumps(payload).encode("utf-8")).replace(b'=', b'')
签名:
- 一个 加密的字符串
# 签名
# 用于加密的 字符串
secret = "+&0p1d$l$lq%xm0qvet0i++!p-*e1ql*5t!kajgjm$fe_ycf1n"
sign_data = "{}.{}".format(header_string.decode("utf-8"), payload_string.decode("utf-8")).encode("utf-8")
sign = base64.urlsafe_b64encode(hmac.new(secret.encode("utf-8"),sign_data,hashlib.sha256).digest())
将三部分用点拼接 生成
print("{}.{}.{}".format(
header_string.decode("utf-8"),
payload_string.decode("utf-8"),
sign.decode("utf-8"),
))
效验 jwt:
from rest_framework import authentication
class JwtAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
jwt = request.META.get("HTTP_AUTHORIZATION")
# 校验 `jwt` 是否合法
# 获取到 `token`
jwt = jwt.encode("utf-8")
# 分割 `jwt`
header_payload_string, sign_string = jwt.rsplit(b'.', 1)
header_string, payload_string = header_payload_string.split(b'.', 1)
header_data = base64.urlsafe_b64decode(header_string)
# 获取到头部数据
header = json.loads(header_data.decode("utf-8"))
# 获取到载荷数据
payload = base64.urlsafe_b64decode(payload_string)
# 获取到签名
signature = base64.urlsafe_b64decode(sign_string)
# 获取到签名算法
alg = header.get('alg')
if not alg:
print("没有算法")
raise ValueError("alg is not null")
secret = "+&0p1d$l$lq%xm0qvet0i++!p-*e1ql*5t!kajgjm$fe_ycf1n".encode("utf-8")
if not hmac.compare_digest(
signature, hmac.new(secret, header_payload_string, hashlib.sha256).digest()
):
print("验证失败")
raise ValueError("验证签名失败")
print("验证通过")
payload_dic = json.loads(payload.decode("utf-8"))
print(payload_dic)
# 过期时间校验
if not payload_dic.get("exp") > int(time.time()):
raise ValueError("jwt过期了")
from django.contrib.auth.models import User
return User.objects.get(pk=payload_dic.get("user_id")), None
直接用模块 生成 jwt:
# 用模块生成
# 秘钥
secret = "+&0p1d$l$lq%xm0qvet0i++!p-*e1ql*5t!kajgjm$fe_ycf1n"
expire_time = int(time.time() + 1) # 1 小时后超时
# 生成 gwt
encoded = jwt.encode(
# header 密钥 编码方式
{'id': 4294967296, 'exp': expire_time}, secret.encode("utf8"), algorithm='HS256')
encoded_str = str(encoded, encoding='utf-8')
print(encoded_str)
time.sleep(2)
# 解码
info = jwt.decode(encoded_str, secret, algorithm='HS256')
print(info)
也可以 使用 django 自带的 jwt
pip install djangorestframework-jwt
其他点:
-
注册 rest_framework app 设置
ALLOWED_HOSTS = ["*"]
为所有 -
在 serializer 序列化器 中 的
validata(self, data)
函数中 获取request
对象并在对象中 获取 请求头 也就是 jwt 的 字符串
def validata(self, data): # 获取 request 对象 request = self.context.get('request') # 获取请求头 也就是 jwt jwt = request.META.get('HTTP_AUTHORIZATION')