jwt
1.1 传统token认证
用户登录,服务器分发一个token,并将其保存在数据库中
当用户在访问时需要携带token,服务端获得token后会去数据库进行校验
1.2 jwt
用户登录,服务器分发一个token,但不保存
当用户在访问时需要携带token,服务端获得token后去校验
相较于传统token,jwt不需要在数据库中保存
1.2.1 jwt认证过程
1.3 python中的jwt
1.3.1 实现原理
-
第一步用户登录时,使用jwt创建一个token返回给客户端
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
注意:jwt由三个部分组成,并且由"."链接
-
第一段:HEADER:ALGORITHM & TOKEN TYPE
json转化为字符串,然后做 base64url 加密
{ "alg": "HS256", # 加密算法 "typ": "JWT" # token类型 }
-
第二段:PAYLOAD:DATA
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "exp":datetime.datetime.utcnow() + datetime.timedelta(minutes=5) # 超时时间 }
注意:荷载主要存储的是用户传来的信息,但是可以被解密出来的,所以像密码之类的敏感信息不要往荷载里放
-
第三段:VERIFY SIGNATURE
第一步:将1,2部分的密文用"."拼接起来 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ 第二步:对前两个部分的密文进行HS256加密+加盐 第三步:对加密后的密文再进行 base64url 加密
注意:最后一段的签名是当有前两段稍微有修改,第三段签名就会变化
-
-
获取到前端传来的token,进行验证
-
第一步,对token进行分割,以"."分割
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
-
第二步:获取第二段,在对其进行base64url解密获得payload,检测是否超时
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "exp":datetime.datetime.utcnow() + datetime.timedelta(minutes=5) # 超时时间 }
-
第三步:将1,2两部分拼接起来,对其进行sha256加密
第一步:将1,2部分的密文拼接起来 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ 第二步:对前两个部分的密文进行HS256加密+加盐 密文 = base64解密(SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c) 如果相等则验证通过,密文未被修改过
-
1.3.2应用
pip install pyjwt
pyjwt里面封装了上面的一些加密解密方法,直接调用即可
pyjwt.encode 生成token
pyjwt.decode token解密
1.3.2.1封装jwt应用
-
创建文件夹,存放生成token的脚本:utils—>jwt_auth
import datetime import jwt from django.conf import settings def create_token(payload,timeout=1): '''创建token''' salt = settings.SECRET_KEY # 构造header headers = { 'typ': 'jwt', 'alg': 'HS256' } # 构造payload,自己在view自定义要传的值,在方法里自动加上超时时间 payload["exp"] = datetime.datetime.utcnow() + datetime.timedelta(minutes=timeout) # 超时时间 token = jwt.encode(payload=payload, key=salt, algorithm="HS256", headers=headers).decode('utf-8') return token
-
创建文件夹,验证token : extensions----->auth
import jwt from django.conf import settings from jwt import exceptions from rest_framework.response import Response from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed class JwtQueryAuthentication(BaseAuthentication): ''' jwt验证 BaseAuthentication:用于拦截请求,在视图函数钱执行相应认证方法 必须重写authenticate(self, request)方法,request参数必须传入 ''' def authenticate(self, request): token = request.query_params.get("token") salt = settings.SECRET_KEY try: payload = jwt.decode(token, salt, True) except exceptions.ExpiredSignatureError: raise AuthenticationFailed({"status": 1003, "error": 'token已失效'}) except jwt.DecodeError: raise AuthenticationFailed({"status": 1004, "error": 'token认证失败'}) except jwt.InvalidTokenError: raise AuthenticationFailed({"status": 1005, "error": '非法的token'}) # if not payload: # return Response({"status": 1003, "error": msg}) return (payload, token) # 有三种操作 # 1.验证失败,直接抛错不继续往下走了 # 2.验证通过返回一个元组(1,2) ,在视图中调用request.user就是元组中的第一个值,调用request.auth是第二个值 # 3.返回None,继续下个验证
-
在视图里
class loginJwtView(APIView): '''封装之后jwt分发token''' # 登录不需要验证 authentication_classes = [] def post(self, request, *args, **kwargs): # 这里简单写了,应该是先经过正常的登录认证在数据库里取到的值 token = create_token({"username": request.data.get("username")}, timeout=2) return Response({"status": 200, "data": token}) from jwtdemo.extensions.auth import JwtQueryAuthentication class orderJwtView(APIView): '''封装之后jwt验证token''' # 会先执行认证 # 在settings设置不需要每个视图都加上authentication_classes # authentication_classes = [JwtQueryAuthentication, ] def get(self, request, *args, **kwargs): print(request.user, request.auth) return Response({"status": 200, "message": "验证成功"})
-
当视图增多时,每次在视图前面加上authentication_classes = [JwtQueryAuthentication, ]便会显得很麻烦,这里在settings里设置
REST_FRAMEWORK = { # 默认情况下,所有的页面都会加上jwt认证 "DEFAULT_AUTHENTICATION_CLASSES":['jwtdemo.extensions.auth.JwtQueryAuthentication',] }
注意:默认情况下,所有的页面都会加上jwt认证,所有登录页面要单独处理,加上authentication_classes = []
2.1 扩展
pip install djangorestframework-jwt
djangorestframework-jwt内部使用的就是pyjwt
3 参考
官网:https://jwt.io/
代码参考:https://www.cnblogs.com/wupeiqi/p/11854573.html