• JWT 实现


    1. 基于传统的JWT认证

    2. jwt

    1. 基于传统的token认证

      用户将用户名和密码发送给服务端,服务端返回其token值(uuid),并将其token值保存入库,以后用户再登录时需要携带token值进行验证。

    例:login

    class Login(APIView):
        def post(self, request, *args, **kwargs):
            '''
            获取用户名密码
            与数据库是否一致
            一致,生成uuid()值,保存数据库,返回给前端
            '''
            username = request.data.get('name')
            passwd = request.data.get('passwd')
            user_object = models.User_info.objects.filter(username=username, password=passwd).first()
            if not user_object:
                return Response({'code': 1001, 'error': '用户名或密码输入有误'})
            random_string = str(uuid.uuid4())
            user_object.token = random_string
            user_object.save()
            return Response({'code': 200, 'data': random_string})
    传统token1,认证 Django

    例:order

    class Order(APIView):
        def get(self, request):
            '''
            query_params   # 获取url中的token值
            '''
            token = request.query_params.get('token')
            if not token:
                return Response({'code': 2002, 'error': '登录成功后才能访问'})
            user_obj = models.User_info.objects.filter(token=token).first()
            if not user_obj:
                return Response({'code': 2001, 'error': 'token不正确'})
            return Response({'code': 200, 'data': '订单列表'})
    传统认证token2

    2. jwt

      用户登录,服务端返回token值(服务端不保存),以后用户再来访问需要携带token,服务端获取token,再做token检验。

      优势: 相较于传统的jwt服务端不需要进行保存。

    3. jwt 实现过程

      用户提交用户名和密码给服务端,如果登录成功,使用jwt创建一个token,并给用户返回。

    eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.    # 第一部分
    eyJpZCI6MSwibmFtZSI6ImFkbWluIiwiZXhwIjoxNjA1NTI3NjkxfQ.  # 第二部分
    n9f7gShAedaVVhq2dkA8ClpJ5TI1AhCogalSqOLCjuU   # 第三部分

    注意:jwt 生产的token是由三段字符串组成,并用 . 连接起来。

      第一段字符串,HEADER, 内部包含算法 (默认哈希256)/和token类型。

      json转化成字符串,然后做base64url加密(base64加密;+ _)

    # 构造header
    headers = {
        'typ': 'jwt',
        'alg': 'HS256'
    }

      第二段字符串,payload,自定义值

      json转化成字符串,然后做base64url加密(base64加密;+ _)

    # 构造payload
    payload = {
        'user_id': user_obj.id,  # 自定义用户ID
        'username': user_obj.username,  # 自定义用户名
        'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=1)  # 超时时间
    }        

      第三段字符串

    第一步:第1,2部分密文拼接起来
    eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwibmFtZSI6ImFkbWluIiwiZXhwIjoxNjA1NTI3NjkxfQ
    
    第二步:对前2部分密文进行HS256加密 + 加盐
    
    第三部:对HS256加密后的密文再做base65url 加密

    以后用户再来访问的时候,需要携带token,后端需要对token进行校验,获取token

      第一步:对token进行切割

    eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.    # 第一部分
    eyJpZCI6MSwibmFtZSI6ImFkbWluIiwiZXhwIjoxNjA1NTI3NjkxfQ.  # 第二部分
    n9f7gShAedaVVhq2dkA8ClpJ5TI1AhCogalSqOLCjuU   # 第三部分

      第二步:对第二部分进行base64url解密,并获取payload信息,检测token是否超时?

    payload = {
        'user_id': user_obj.id,  # 自定义用户ID
        'username': user_obj.username,  # 自定义用户名
        'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=1)  # 超时时间
    }    

      第三步:把第1,2端拼接,再次执行sha256加密。

    第一步:第1,2部分密文拼接起来
    eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwibmFtZSI6ImFkbWluIiwiZXhwIjoxNjA1NTI3NjkxfQ
    
    第二步:对前2部分密文进行HS256加密 + 加盐
    
    密文 = base64解密(n9f7gShAedaVVhq2dkA8ClpJ5TI1AhCogalSqOLCjuU)

    例: 代码演示

    pip install pyjwt
    安装 jwt
    from rest_framework.response import Response
    from rest_framework.views import APIView
    class Jwt_login(APIView):
        def post(self, request, *args, **kwargs):
            username = request.data.get('username')
            password = request.data.get('password')
            SALT = 'qpo0rerngnejg3n5j3nk2'
            if not all([username, password]):
                return Response({'code': 10010, 'error': '输入完整的用户名密码'})
            user_obj = models.User_info.objects.filter(username=username, password=password).first()
            if not user_obj:
                return Response({'code': 10020, 'error': '用户名密码不正确'})
            # 构造header
            headers = {
                'typ': 'jwt',
                'alg': 'HS256'
            }
            # 构造payload
            payload = {
                'user_id': user_obj.id,  # 自定义用户ID
                'username': user_obj.username,  # 自定义用户名
                'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=1)  # 超时时间
            }
            result = jwt.encode(payload=payload, key=SALT, algorithm="HS256", headers=headers).decode('utf-8')
            return Response({'code': 200, 'data': result})
    login 代码演示
    from jwt import exceptions
    class Jwt_order(APIView):
        def get(self, request, *args, **kwargs):
            token = request.query_params.get('token')
            SALT = 'qpo0rerngnejg3n5j3nk2'
            msg = None
            if not token:
                return Response({'code': 3001, 'error': 'token值不能为空'})
            try:
                payload = jwt.decode(token, SALT, True)
                return Response({'code':200,'data':payload})
            except exceptions.ExpiredSignatureError:
                msg = 'token已失效'
            except jwt.DecodeError:
                msg = 'token认证失败'
            except jwt.InvalidTokenError:
                msg = '非法的token'
            return Response({'code':20020,'data':msg})
    order 代码演示

    例: 代码演示2升级

    # 在子应用中新建文件夹,新建py,将返回token值的代码写入
    
    from app01.utils.jwt_auth import create_token
    class Pro_login(APIView):
        authentication_classes = []
        def post(self, request, *args, **kwargs):
            username = request.data.get('username')
            password = request.data.get('password')
            if not all([username, password]):
                return Response({'code': 10010, 'error': '输入完整的用户名密码'})
            user_obj = models.User_info.objects.filter(username=username, password=password).first()
            if not user_obj:
                return Response({'code': 10020, 'error': '用户名密码不正确'})
            token = create_token({'id':user_obj.id,'name':user_obj.username},1)
            return Response({'code': 200, 'data': token})
    views.py(login)
    # 子应用返回token代码如下
    
    from django.conf import settings
    import jwt
    import datetime
    from rest_framework.response import Response
    def create_token(payload,timeout):
        SALT = settings.SECRET_KEY
        # 构造header
        headers = {
            'typ': 'jwt',
            'alg': 'HS256'
        }
        # 构造payload
        payload['exp'] =  datetime.datetime.utcnow() + datetime.timedelta(minutes=timeout)  # 超时时间
        result = jwt.encode(payload=payload, key=SALT, algorithm="HS256", headers=headers).decode('utf-8')
        return result
    jwt_auth.py
    # 在子应用新建文件夹,新建py,将token认证的代码写入
    
    from app01.extensions.auth import JwtQueryParamsAuthentication
    class Pro_order(APIView):
        authentication_classes = [JwtQueryParamsAuthentication, ] # 类似于Django中间件,先执行这个
        def get(self, request,*args, **kwargs):
            print(request.user)
            print(request.auth)
            return Response('订单列表')
    views.py(order)
    from django.conf import settings
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from rest_framework.response import Response
    import jwt
    from jwt import exceptions
    '''
    获取token并判断token的合法性
    1. 切割
    2. 解密第二段/判断过期
    3. 验证第三段的合法性
    '''
    class JwtQueryParamsAuthentication(BaseAuthentication):
        def authenticate(self, request):
            token = request.query_params.get('token')
            SALT = settings.SECRET_KEY
            if not token:
                raise AuthenticationFailed({'code':'token不存在'})
            try:
                payload = jwt.decode(token, SALT, True)
            except exceptions.ExpiredSignatureError:
                raise AuthenticationFailed({'code':1,'error':"token已失效"})
            except jwt.DecodeError:
                raise AuthenticationFailed({'code':2,'error':"token认证失败"})
            except jwt.InvalidTokenError:
                raise AuthenticationFailed({'code':3,'error':"非法的token"})
    
            return (payload, token)
    
    
        # 三种操作
        # 1. 抛出异常,后续不执行
        # 2. return一个元组 (1,2),认证通过,在视图中如果调用request.user就是调用第一个值,request.auth就是元祖的第二个值
        # 3. None 表示下一次认证
    auth.py

    例:代码演示3 全局

    from app01.utils.jwt_auth import create_token
    class Pro_login(APIView):
        authentication_classes = []   # j加入后表示不受全局限制
        def post(self, request, *args, **kwargs):
            username = request.data.get('username')
            password = request.data.get('password')
            if not all([username, password]):
                return Response({'code': 10010, 'error': '输入完整的用户名密码'})
            user_obj = models.User_info.objects.filter(username=username, password=password).first()
            if not user_obj:
                return Response({'code': 10020, 'error': '用户名密码不正确'})
            token = create_token({'id':user_obj.id,'name':user_obj.username},1)
            return Response({'code': 200, 'data': token})
    
    
    
    from app01.extensions.auth import JwtQueryParamsAuthentication
    class Pro_order(APIView):
        # authentication_classes = [JwtQueryParamsAuthentication, ] # 类似于Django中间件,先执行这个
        def get(self, request,*args, **kwargs):
            print(request.user)
            print(request.auth)
            return Response('订单列表')
    View.py

    用户登录返回token,与认证token不变。

    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES" : ["app01.extensions.auth.JwtQueryParamsAuthentication", ]
    }
    
    # 在APIView 中底层有DEFAULT_AUTHENTICATION_CLASSES方法。
    settings

    图片路径如下:

  • 相关阅读:
    2014-3-10 时间都去哪了,还没好好感受年轻就老了
    2014-3-4 思杨昨天已经顺利到老家 --------- 回忆思杨之2--“叫你不穿鞋鞋”
    2014-3-4 鬼脸笑笑的思杨
    路由
    视图
    请求与响应
    序列化组件
    APIView源码分析
    CBV源码分析
    DRF入门规范
  • 原文地址:https://www.cnblogs.com/xinzaiyuan/p/13988222.html
Copyright © 2020-2023  润新知