四:认证、权限、频率组件使用

一、认证

认证涉及登录,登录为一次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次
    }
}

 

posted @ 2020-03-22 20:05  aikell  阅读(182)  评论(0编辑  收藏  举报