标签:可扩展性 失败 方法 work code salt 校验 加密原理 token
1. 用户第一次登录时, 生成一个token并返回给前台, 同时将其与用户主键一同存在后台服务器上(数据库或缓存中)
2. 下一次访问需要登录的页面时, 将token一起传入
3. 后台拿着token去数据库或缓存中查找是否存在该token, 存在则认证通过, 否则认证不通过
1. token存在后台, 增加了存储和读取的开销
2. 当存在多个后台服务器时, 需同步共享token, 比较麻烦
1. 用户第一次登录时, 生成一个token, 但后台不存储该token
2. 下一次访问需要登录的页面时, 将token一起传入
3. 后台拿着token进行解析和校验, 若解析成功则认证通过, 否则认证不通过
生成的token分为三个部分: HEADER.PAYLOAD.SIGNATURE, 这三个部分都是可逆算法base64加密后的字符串, 最后用点号(.)拼接.如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
代表了加密算法和token类型, 若不显示指定, 默认为:
{
"alg": "HS256",
"typ": "JWT"
}
加密后结果为: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
代表了想要传输的业务信息和token的过期时间(可选), 例如:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 451154141
}
加密后结果为: eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
JWT的关键, 其规则是将前面两段加密后的密文再加上自定义的盐值一起拼接后, 再通过不可逆算法HS256(具体使用的是HEADER中的算法)进行加密, 最后再对该密文进行可逆算法base64加密
盐值(salt): 指的是加密时加入的自定义的字符串, 最好是随机或者杂乱的字符串, 这样更能够确定加密后字符串的唯一性, django中可以使用settings中的SECRET_KEY
加密后结果为: SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
拿到请求中传过来的token后:
1. 按.号拆分token, 拿到三段值
2. 对这三段密文进行base64解密, 从明文中拿到加密算法和业务数据以及过期时间
3. 再次拼接前两段的密文和自定义的盐值(该盐值必须和创建token时的盐值一样), 使用HEADER中的算法进行加密
4. 将加密的结果和拿到的token中的第三段解密的明文进行比较, 若完全一致则说明认证成功, 若不一致则说明token被篡改过, 认证失败
JWT的根本思想就是将业务数据通过不可逆算法加密存储在token中, 那么为什么要搞成三段式这么复杂呢?
直接把业务数据加上盐值, 然后用默认不可逆算法生成一段密文字符串进行传输不就可以了吗?
这样加密时是比较简单, 但是解密时却由于不可逆算法而拿不到其中的业务数据, 所以确实需要再加一段式来单独存储业务数据
JWT又加了一段用来存储加密算法, 能够让使用者自己确定具体使用什么算法进行加密, 增加了可扩展性
这是python使用JWT的基础包, 在jwt官网中python语言点赞最多的就是pyjwt, 安装方式为: pip install pyjwt , 这个包已经把加密和解密的逻辑写好了, 我们只需要传入加密算法/业务数据/盐值即可
定义两个接口, 登录(login)和查看订单(order), 只有登录过的用户才能成功访问查看订单接口, 我们可以在登录接口中若成功登录则返回jwt的加密token, 在订单接口中自定义一个认证类, 在认证类中校验token
from django.urls import path from users import views urlpatterns = [ path(‘login/‘, views.LoginView.as_view()), path(‘order/‘, views.OrderView.as_view()), ]
1. 登录成功后, 调用获取token的方法 create_token() , 传入参数为用户信息和token过期时间(单位: 分钟), 默认1分钟
2. 在订单视图类中设置认证类 JWTAuthentication
3. create_token和JWTAuthentication都定义在utils包的JWTAuth.py中
from rest_framework.views import APIView
from rest_framework.response import Response
from utils.JWTAuth import create_token, JWTAuthentication
class LoginView(APIView): def post(self, request, *args, **kwargs): # 获取用户名密码 name = request.data.get(‘name‘) pwd = request.data.get(‘pwd‘) # 获取User对象 try: user = models.User.objects.filter(name=name, pwd=pwd).first() except Exception: return Response({‘status‘: 1, ‘errmsg‘: ‘用户名或密码不正确!‘}) # 获取token token = create_token({‘id‘: user.id, ‘name‘: user.name}, 1) # 返回成功响应 return Response({‘status‘: 0, ‘token‘: token}) class OrderView(APIView): authentication_classes = [JWTAuthentication, ] def get(self, request): return Response({‘status‘: 0, ‘msg‘: ‘ok‘})
import jwt from jwt import exceptions as JWTException from django.conf import settings import datetime from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed def create_token(payload, timeout=1): # 给传过来的业务数据增加一个过期时间限制 payload[‘exp‘] = datetime.datetime.utcnow() + datetime.timedelta(minutes=timeout) # 定义盐值 salt = settings.SECRET_KEY # 默认不可逆加密算法为HS256 token = jwt.encode(payload=payload, key=salt) return token class JWTAuthentication(BaseAuthentication): def authenticate(self, request): # 从url参数中获取token token = request.query_params.get(‘token‘) # 盐值 salt = settings.SECRET_KEY # 解码token try: payload = jwt.decode(jwt=token, key=salt, verify=True) except JWTException.ExpiredSignature: raise AuthenticationFailed(‘token已失效‘) except jwt.DecodeError: raise AuthenticationFailed(‘token认证失败‘) except jwt.InvalidTokenError: raise AuthenticationFailed(‘非法的token‘) return payload.get(‘name‘), token
注意: 设置过期时间时, 一定是在payload段中设置, 且键名固定为‘exp‘, 值为 datetime.datetime.utcnow() + datetime.timedelta(xxxx)
python-JWT(Json Web Token)-pyjwt
标签:可扩展性 失败 方法 work code salt 校验 加密原理 token
原文地址:https://www.cnblogs.com/gcxblogs/p/13376058.html