1. 程式人生 > 實用技巧 >drf 訪問頻率限制

drf 訪問頻率限制

頻率限制

   一個網站的訪問頻率限制是非常重要的,訪問頻率限制做的好可以預防爬蟲等惡意行為。

   使用drf的頻率限制對網站介面訪問做出限制十分的便捷好用,你只需要直接進行配置即可。

內建限制

區域性使用

   首先我們在檢視中進行配置:

from rest_framework.throttling import UserRateThrottle  # 已登入的
from rest_framework.throttling import AnonRateThrottle  # 未登入的
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from django.contrib import auth

class ThrottleTestAPI(GenericAPIView):
    throttle_classes = [UserRateThrottle,AnonRateThrottle]

    def get(self,request):
        if request.user.is_authenticated:  # 使用者一登陸
            return Response(data="你的請求次數有十次每分鐘")
        return Response("你的請求次數只有三次每分鐘")
        
    def post(self,request):
        user_obj = auth.authenticate(username="admin",password="admin123")
        auth.login(request,user_obj)
        return Response("登入成功了")

   其次,針對已登入的使用者和未登入的使用者,可以在settings中進行配置限制次數:

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {  # 限制次數 , 未登入使用者一分鐘最多三次,登入使用者最多一分鐘十次
        'anon': '3/m',  # 會去配置的 UserRateThrottle 以及 AnonRateThrottle 中找到屬性 scope ,scope對應的就是生效的配置
        'user': '10/m'
    }
}

全域性使用

   如果全域性使用,則可以進行如下配置:

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ),
    'DEFAULT_THROTTLE_RATES': {  # 限制次數 , 未登入使用者一分鐘最多三次,登入使用者最多一分鐘十次
        'anon': '3/m',  # 會去配置的 UserRateThrottle 以及 AnonRateThrottle 中找到屬性 scope ,scope對應的就是生效的配置
        'user': '10/m'
    }
}

   如果想針對某一個檢視取消全域性配置,則將throttle_classes設定為空列表即可:

class ThrottleTestAPI(GenericAPIView):
    throttle_classes = []

    def get(self,request):
        if request.user.is_authenticated:
            return Response(data="你的請求次數有十次每分鐘")
        return Response("你的請求次數只有三次每分鐘")

    def post(self,request):
        user_obj = auth.authenticate(username="admin",password="admin123")
        auth.login(request,user_obj)
        return Response("登入成功了")

自定製限制

頻率限制原理

   drf中的頻率控制基本原理是基於訪問次數和時間的,當然我們可以通過自己定義的方法來實現。當我們請求進來,走到我們頻率元件的時候,drf內部會有一個字典來記錄訪問者的IP

   以這個訪問者的IPkeyvalue為一個列表,value裡面存放訪問者每次訪問的時間,如下:

   { IP1: [第三次訪問時間,第二次訪問時間,第一次訪問時間],}

   把每次訪問最新時間放入列表的最前面,記錄這樣一個數據結構後,通過什麼方式限流呢

   如果我們設定的是10秒內只能訪問5次,

  1. 判斷訪問者的IP是否在這個請求IP的字典裡
  1. 保證這個列表裡都是最近10秒內的訪問的時間。判斷當前請求時間和列表裡最早的(也就是最後的)請求時間的如果差大於10秒,說明請求以及不是最近10秒內的,刪除掉繼續判斷倒數第二個,直到差值小於10秒
  1. 判斷列表的長度(即訪問次數),是否大於我們設定的5次,如果大於就限流,否則放行,並把時間放入列表的最前面。

自定義限制

   使用自定義限制時,需要建立一個類並且重寫allow_request()以及wait()方法。

   allow_request()有兩個額外的引數,分別是二次包裝後的request物件,以及例項化過後的檢視類本身,當頻率限制通過後返回True,否則返回False

   wait()方法是在return False後觸發,必須返回一個int型別的值來回複頻率限制還有多久取消。

class RequestLimit:
    request_dict = {}
    def __init__(self):
        self.expiration = None
        self.count = 3  # 設定最大訪問次數
        self.seconds = 10  # 設定過期時間,秒為單位

    def allow_request(self, request, view):  # 自動呼叫該方法
        # 拿出IP
        ip = request.META.get("REMOTE_ADDR")
        import time
        current_time = time.time()
        # 如果ip不在字典中,則新增即可,代表當前已訪問了一次
        if not ip in self.request_dict:
            self.request_dict[ip] = [current_time]
            return True
        # 如果在,判斷長度是否等於設定的最大訪問次數
        if len(self.request_dict[ip]) == self.count:
            # 如果等於最大訪問次數,則判斷最後一位的時間和當前時間相差是否大於指定的過期時間
            if current_time - self.request_dict[ip][-1] > self.seconds:
                # 如果大於,清空寫入
                self.request_dict[ip].clear()
                self.request_dict[ip].append(current_time)
                return True
            else:
                # 如果不大於,說明時間還沒過,暫時不能訪問,設定多少秒後才能訪問
                self.expiration = self.request_dict[ip][-1] + self.seconds
                return False
        # 如果在,長度不大於3,則追加當前時間
        self.request_dict[ip].append(current_time)
        return True

    def wait(self):
        import time
        current_time = time.time()
        result = self.expiration - current_time  # 用過期的時間,減去當前的時間
        return result

區域性使用

   直接進行區域性使用即可,不要再到專案全域性資料夾下的settings.py中做額外的配置了。

class ThrottleTestAPI(GenericAPIView):
    throttle_classes = [app01.app01_throttle.RequestLimit]  # 直接使用即可

    def get(self,request):
        return Response("get...")

f

全域性使用

   全域性使用也不用再規定過期時間,直接在settings.py中配置使用即可:

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES':['app01.app01_throttle.RequestLimit',],
}