zoukankan      html  css  js  c++  java
  • 四:认证、权限、频率组件使用

    一、认证

    认证涉及登录,登录为一次post请求,则涉及跨站请求伪造,django中间件csrf很好的解决了这个问题,但是在前后端分离开发的过程中,使用Postman发送请求无法使用该组件,所以将该中间件注释后,自己简单实现一个类似csrftoken的功能:

      登录后需要验证token值,每次登录,token值都会变。登录后,所有请求必须带着token值才能通过验证

    import hashlib
    import time
    # 利用摘要算法生成一条token值,采用加盐方式
    class GetToken(object):
    
        def __init__(self, username):
            self.username = username
    
        def get_md5_token(self):
            md = hashlib.md5(bytes(self.username, encoding="utf-8"))
            md.update(bytes(str(time.time()), encoding="utf-8"))
            return md.hexdigest()
    
    # 登录视图
    class LoginView(APIView):
        def post(self, request):
            username = request.data.get('username')
            password = request.data.get('password')
            obj = models.User.objects.filter(username=username, password=password).first()
            res = {"state": 200, "msg": None}
            if obj:
                token = GetToken(obj.username)
                str_token = token.get_md5_token()
                models.Token.objects.update_or_create(user=obj, defaults={"token": str_token})
                res["token"] = str_token
            else:
                res["state"] = 404
                res["msg"] = "登录失败"
            return Response(res)

      认证类:

    from app01 import models
    from rest_framework import exceptions
    
    # 认证类
    class Authentication(object):
    
        def authenticate(self, request):
            token = request.GET.get('token')
            obj = models.Token.objects.filter(token=token).first()
            if obj:
                return obj.user.username, obj.token
            else:
                raise exceptions.AuthenticationFailed('验证失败')
    
        # 不加这个方法会报错
        def authenticate_header(self, request):
            pass

      为其它视图添加认证组件:

    from rest_framework import viewsets
    from app01 import utils
    
    class BookView(viewsets.ModelViewSet):
        authentication_classes = [utils.Authentication, ]  # 认证组件,一个列表,类别中放认证类
        queryset = models.Book.objects.all()
        serializer_class = serializers.BookSerializers

      全局视图添加认证类,需要在settings中配置:

    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.utils.Authentication",]
    }

      如果添加了全局,某个视图不许要认证,只需要在视图中设置为空即可:

    authentication_classes = []

    2、权限

      一条url代表一条权限,所有权限类验证时,应该是取到用户所有的url后,根据访问的地址进行验证,这里就不详细示例:

    class User(models.Model):
        username = models.CharField(max_length=48)
        password = models.CharField(max_length=64)
        roles_choices = ((1, "普通用户"), (2, "管理员"), (3, "超级管理员"))
        role = models.IntegerField(choices=roles_choices, default=1)
    
        def __str__(self):
            return self.username
    # 权限类
    class PermissionCheck(object):
        message = "你不是超级管理员"
        # 这里的request属于APIview重新构造的reuqest,经过认证后,认证组件返回一个request.user和request.auth,
        # 这两个的返回结果在自己的认证类中自定义返回的结果,所以这里能够直接调用
        def has_permission(self, request, view):
            if request.user.role == 3:
                return True
            else:
                return False
    class BookView(viewsets.ModelViewSet):
        authentication_classes = [utils.Authentication, ]  # 认证组件,一个列表,类别中放认证类
        permission_classes = [utils.PermissionCheck, ]  # 权限组件, 一个列表, 列表中放权限类
        queryset = models.Book.objects.all()
        serializer_class = serializers.BookSerializers

      全局视图加入权限验证需要在settings中配置:

    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.utils.Authentication",], # 认证
        "DEFAULT_PERMISSION_CLASSES":["app01.utils.PermissionCheck",] # 权限
    }

    3、频率

      rest_framework提供内置throttle类,继承BaseThrottle

    from rest_framework.throttling import BaseThrottle
    
    VISIT_RECORD = {}  # {IP地址:[12:23:05, 12:22:45, 12:22:30]}
    
    class VisitThrottle(BaseThrottle):
    
        def __init__(self):
            self.history = None
    
        def allow_request(self, request, view):
            remote_addr = request.META.get('REMOTE_ADDR')  # 获取IP地址
            print(remote_addr)
            # xff = request.META.get('HTTP_X_FORWARDED_FOR')
            # print(xff)
            import time
            ctime = time.time()
            # 第一次访问
            if remote_addr not in VISIT_RECORD:  # IP地址不在VISIT_RECORD地点当中
                VISIT_RECORD[remote_addr] = [ctime, ]  # {IP地址:[12:23:05, ]}
                return True     # 第一次访问通过频率验证
            
            # 不是第一次访问
            history = VISIT_RECORD.get(remote_addr)  # 获取访问的时间列表[12:23:07, 12:23:05]
            self.history = history
            
            # 访问时间列表有值并且最久一次访问的时间距离现在时间已经过去60秒,把这个时间拿出来
            while history and history[-1] < ctime - 60:
                history.pop()  # 因为列表pop操作只能把最后一条数据取出,所以列表中的访问时间应该是把最近一次访问时间放在列表最前
            
            # 如果60秒内访问此时小于3次,把这次访问的时间添加到列表最前面。如果大于3次,限制访问
            if len(history) < 3:
                history.insert(0, ctime)
                return True
            else:
                return False
        
        # 距离限制还有多久
        def wait(self):
            import time
            ctime = time.time()
            return 60 - (ctime - self.history[-1])
    from rest_framework import viewsets
    from app01 import utils
    
    class BookView(viewsets.ModelViewSet):
        authentication_classes = [utils.Authentication, ]  # 认证组件,一个列表,类别中放认证类
        permission_classes = [utils.PermissionCheck, ]  # 权限组件, 一个列表, 列表中放权限类
        throttle_classes = [utils.VisitThrottle, ]   # 频率组件
        queryset = models.Book.objects.all()
        serializer_class = serializers.BookSerializers

      全局视图设置频率:

    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.utils.Authentication",], # 认证
        "DEFAULT_PERMISSION_CLASSES":["app01.utils.PermissionCheck",], # 权限
    "DEFAULT_THROTTLE_CLASSES":["app01utils.VisitThrottle",]  # 频率
    }

      上面是内置频率方法实现原理,使用内置频率方法:

    class VisitThrottle(SimpleRateThrottle):
    
        scope="visit_rate"  # settings中的标识
        def get_cache_key(self, request, view):
    
            return self.get_ident(request)  #get_ident方法是获取用户IP地址,你也可以返回用户名request.user.username

      需要在settings中设置:

    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.utils.Authentication",], # 认证
        "DEFAULT_PERMISSION_CLASSES":["app01.utils.PermissionCheck",], # 权限
        "DEFAULT_THROTTLE_CLASSES":["app01utils.VisitThrottle",],  # 频率
        "DEFAULT_THROTTLE_RATES":{
            "visit_rate":"5/m",  # m代表分钟,每分钟限制访问5次
        }
    }
  • 相关阅读:
    java.lang.NoSuchMethodError
    asm相关内容想下载(包括 jar 包)
    Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/objectweb/asm/Type
    用Navicat连接mysql报错:2003-Can't connect to MySql server on '10.100.0.109'(10039)
    The type java.lang.reflect.AnnotatedElement cannot be resolved. It is indirectly referenced from required .class files
    The type java.lang.CharSequence cannot be resolved. It is indirectly referenced from required .class files
    交通测速方式
    卡口和电子警察的区别
    Myeclipse连接Mysql数据库时报错:Error while performing database login with the pro driver:unable
    在window上安装mysql
  • 原文地址:https://www.cnblogs.com/aizhinong/p/12547975.html
Copyright © 2011-2022 走看看