• DRF之JWT补充


    DRF之JWT补充

    1.JWT控制用户登录后才能反问,匿名用户无法访问

    class QueryUserView(GenericViewSet, RetrieveModelMixin):
        """
        查询接口
        """
        queryset = User.objects.all()
        serializer_class = UserSerializer
        pk = None
        # throttle_classes = [IPThrottle, ]
        authentication_classes = [LoginToken, ]
        permission_classes = [IsAuthenticated, ]
    
        def get_object(self):
            queryset = self.filter_queryset(self.get_queryset())
            filter_kwargs = {self.lookup_field: self.pk}
            return queryset.filter(**filter_kwargs)
    
        def get_queryset(self):
            queryset = self.queryset
            if isinstance(queryset, QuerySet):
                queryset = queryset.all()
            return queryset.filter(is_delete=False)
    
        def retrieve(self, request, *args, **kwargs):
            self.pk = kwargs.get('pk')
            if not self.pk:
                return APIResponse(code='102', msg='查询失败', data={'result': '缺少主键值'})
            if not self.get_object():
                return APIResponse(code='102', msg='查询失败', data={'result': '无效的主键值'})
            instance = self.get_object().first()
            serializer = self.get_serializer(instance=instance)
            return APIResponse(code='102', msg='查询成功', data=serializer.data)
    
        def query(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    

    不携带token或者token失效无法登录成功

    不携带token或者token失效,系统会默认你为匿名用户
    

    image-20200715193301080

    先进行登录,获取token值。携带正确的token可以登录成功

    image-20200715193448726

    将权限校验去掉后,即使携带正确的token也登录失败,因为系统此时默认你为匿名用户

    2.控制登录接口返回数据的格式

    方案一:自己写登录接口

    方案二:用内置控制登录接口返回数据的格式

    JWT配置信息中有这个属性
    JWT_AUTH = {
        'JWT_RESPONSE_PAYLOAD_HANDLER':
        'rest_framework_jwt.utils.jwt_response_payload_handler',
    }
    # 重写jwt_response_payload_handler,然后进行替换
    

    自定义基于JWT的权限认证

    # 自定义基于jwt的权限认证
    
    from rest_framework.authentication import BaseAuthentication
    from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
    
    from rest_framework.exceptions import AuthenticationFailed
    from rest_framework_jwt.utils import jwt_decode_handler
    from rest_framework_jwt.utils import jwt_encode_handler
    
    import jwt
    
    from app01 import models
    
    
    class XJwtAuthentication(BaseJSONWebTokenAuthentication):
        def authenticate(self, request):
            jwt_value = str(request.META.get('HTTP_AUTHORIZATION')) # 获取token信息
            if jwt_value:
                try:
                    payload = jwt_decode_handler(jwt_value)      # 取出payload
                except jwt.ExpiredSignatureError:
                    raise AuthenticationFailed('签名过期')
                except jwt.InvalidTokenError:
                    raise AuthenticationFailed('非法用户')
                except Exception as e:
                    raise AuthenticationFailed(str(e))
                """
                得到user对象:
                第一种方法去数据库查,
                第二种不查数据库
                """
                # user = models.User.object.get(pk=payload.get('user_id')) 第一种
                user = models.User(id=payload.get('user_id'),username=payload.get('username'))
                return user,jwt_value
            else:
                raise AuthenticationFailed('没有携带token认证信息')
    

    jwt提供了通过三段token,取出payload的方法,并且有校验功能

    class XJwtAuthentication(BaseJSONWebTokenAuthentication):
        def authenticate(self, request):
            jwt_value=request.META.get('HTTP_AUTHORIZATION')
            if jwt_value:
                try:
                #jwt提供了通过三段token,取出payload的方法,并且有校验功能
                    payload=jwt_decode_handler(jwt_value)
                except jwt.ExpiredSignature:
                    raise AuthenticationFailed('签名过期')
                except jwt.InvalidTokenError:
                    raise AuthenticationFailed('用户非法')
                except Exception as e:
                    # 所有异常都会走到这
                    raise AuthenticationFailed(str(e))
                user=self.authenticate_credentials(payload)
                return user,jwt_value
            # 没有值,直接抛异常
            raise AuthenticationFailed('您没有携带认证信息')
    

    3.手动签发token来实现多方式的登录

    # 使用用户名/手机号/邮箱都可以登录
    # 前端需要传回来的json格式数据
    {
        "username":"surpass/18395806407/18395806407@163.com"
    }
    

    LoginSerializer

    from rest_framework.exceptions import ValidationError
    from rest_framework_jwt.utils import jwt_encode_handler, jwt_payload_handler
    import re
    
    class LoginSerializer(serializers.ModelSerializer):
        username = serializers.CharField(max_length=32)  # 覆盖username
    
        class Meta:
            model = models.User
            fields = ['username', 'password']
    
        def validate(self, attrs):
            username = attrs.get('username')
            password = attrs.get('password')
            if re.match('^1[3|4|5|7|8][0-9]{9}$', username):
                user = models.User.objects.filter(mobile=username)
            elif re.match('^.+@.+$', username):
                user = models.User.objects.filter(email=username)
            else:
                user = models.User.objects.filter(username=username)
            if not user:
                raise ValidationError('用户不存在')
            if not user.check_password(password):
                raise ValidationError('密码错误')
            payload = jwt_payload_handler(user)  # 传入user得到payload
            token = jwt_encode_handler(payload)  # 传入payload得到token 即jwt_value
            self.context['token'] = token   # 利用context返回token和user
            self.context['user'] = user
            return attrs
    

    LoginView

    class LoginView(GenericViewSet):
        queryset = User.objects.all()
        serializer_class = LoginSerializer
    
        def login(self, request, *args, **kwargs):
            login_ser = self.get_serializer(data=request.data, context={})
            login_ser.is_valid(raise_exception=True)
            token = login_ser.context.get('token')
            user = login_ser.context.get('user')
            return APIResponse(code=100, msg='登录成功', data={'token': token, 'username': user.username})
    

    JWT的配置参数

    # 使用内置的obtain_jwt_token时使用
    JWT_AUTH = {
        'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.utils.jwt_response_payload_handler',
        'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),  # 过期时间,手动配置
    }
    
    # utils.py
    def jwt_response_payload_handler(token, user=None, request=None):
        return {
            'token': token,
            'msg': '登录成功',
            'status': 100,
            'username': user.username
        }
    

    4.基于角色的权限控制(django内置的auth模块)

    RBAC(Role-Based Access Control):基于角色的访问控制,通常用于公司的内部系统

    # django的auth就是内置了一套关于RBAC的权限系统
    核心是:通过角色绑定权限,然后给用户分配角色
    在这种访问机制中,引入了role的概念,将用户与权限之间的关系进行了解耦,让用户通过角色与权限进行关联。在RBAC模型中,(who,what,how)构成了访问权限的三元组。
    
    # 在django中
    	# 后台的权限控制(公司内部系统,crm,erp,协同平台)
    	user表
        permssion表
        group表
        user_groups表是user和group的中间表
        group_permissions表是group和permssion中间表
        user_user_permissions表是user和permission中间表
        # 前台(主站),需要用三大认证
    

    5 .Django缓存

    • 前后端混合开发缓存的位置
    # 缓存的位置,通过配置文件来操作(以文件为例子)
    # 缓存的粒度:全站缓存,单页面缓存,以及页面局部缓存
    

    页面局部缓存:

    <body>
    <div>
        {{ t }}<hr>
        {% load cache %}
        {% cache 5 'name' %}
            {{ time }}
        {% endcache %}
    </div>
    </body>
    

     单页面缓存:

    # 给视图函数加上装饰器
    from django.views.decorators.cache import cache_page
    
    
    @cache_page(3)  # 缓存10秒中
    def test2(request):
        import time
        time = time.time
        back_dict = {'time': time}
        return render(request, 'test2.html', back_dict)
    

    全站缓存:

    # 在中间件设置
    MIDDLEWARE = [
        'django.middleware.cache.UpdateCacheMiddleware',
        ...
        'django.middleware.cache.FetchFromCacheMiddleware',
    ]
    CACHE_MIDDLEWARE_SECONDS=10  # 全站缓存时间
    
    • 前后端分离项目缓存的使用

      # 使用步骤:
      # views.py
      from django.core.cache import cache
      
      def test1(request):
          import datetime
          cache.set('time',datetime.datetime.now())
          return render(request, 'test1.html')
      
      
      def test2(request):
          time = cache.get('time')
          back_dict = {'time':time}
          return render(request, 'test2.html', back_dict)
      
      # 应用场景
      第一次查询所有图书,你通过多表联查序列化之后的数据,直接缓存起来
      后续,直接先去缓存查,如果有直接返回,没有,再去连表查,返回之前再缓存
      
  • 相关阅读:
    如何写出无法维护的代码
    阅读优秀代码是提高开发人员修为的一种捷径
    防止代码变质的思考与方法
    干掉你程序中的僵尸代码
    如何防止代码腐烂
    迈出单元测试的第一步
    使用VC6.0编译C++代码的时候报错:fatal error C1071: unexpected end of file found in comment(Mark ZZ)
    Android中onTouch方法的执行过程以及和onClick执行发生冲突的解决办法
    Java中JNI的使用详解第三篇:JNIEnv类型中方法的使用
    Java中JNI的使用详解第二篇:JNIEnv类型和jobject类型的解释
  • 原文地址:https://www.cnblogs.com/surpass123/p/13308478.html
Copyright © 2020-2023  润新知