03-Django REST framwork 板塊(03-認證、權限、節流)

1. 用戶登錄認證

  • a. 有些API需要用戶登錄成功之後,才能訪問;有些無需登錄就能訪問。

  • b. 基本使用認證組件
    解決思路:
    a. 創建兩張表(用戶表,token表)
    b. 用戶登錄(返回token並保存到數據庫)

  • c.涉及兩種操作(全局認證,單獨認證)

  • e. 使用框架內置認證類(自己寫認證類的時候繼承自BaseAuthentication這個類,更規範,不用自己從頭搞)

      1. 認證類,必須繼承:from rest_framework.authentication import BaseAuthentication
      1. 其他認證類:BasicAuthentication(可以瀏覽器自動生成非模態對話框的一種認證類,SessionAuthentication等等,這些都是基於BaseAuthentication瀏覽器幫助加密,放到請求頭裏邊傳過去得到,基本用不到)
    • 3.大多場景下,繼承自BaseAuthentication,然後自己定義認證類
      基類如下:
class BaseAuthentication(object):
    """
    All authentication classes should extend BaseAuthentication.
    """

    def authenticate(self, request):
        """
        Authenticate the request and return a two-tuple of (user, token).
        """
        raise NotImplementedError(".authenticate() must be overridden.")

    def authenticate_header(self, request):   # 認證失敗時,返回的響應頭的定義
        """
        Return a string to be used as the value of the `WWW-Authenticate`
        header in a `401 Unauthenticated` response, or `None` if the
        authentication scheme should return `403 Permission Denied` responses.
        """
        pass

認證梳理:

1. 使用

  • 創建類:繼承BaseAuthentication; 實現:authenticate方法,其中authenticate_header方法,不用寫,直接按基類pass就行

  • authenticate的返回值:

    • 1.返回None,跳過,讓下一認證來執行。
    • 2.返回(拋出)異常(這裏上邊類有隊異常的接收跟處理)raise exceptions.AuthenticationFailed(用戶認證失敗 from rest_framework import exceptions)
    • 3.返回一個元組(元素1,元素2) # 元素1賦值給request.user; 元素2賦值給request.auth,可以在views.py視圖中對返回的兩個,進行調用,比如拿到驗證通過的對象user(前提定義函數時,得返回user相關的信息)
  • 局部使用(局部針對某些類進行驗證,針對單獨的類添加靜態字段)

from rest_framework.authentication import BaseAuthentication,BasicAuthentication

class UserInfoView(APIView):
    """
    訂單相關業務
    """
    authentication_classes = [BasicAuthentication,]   # 設置這靜態字段,單獨設置
    def get(self,request,*args,**kwargs):
        print(request.user)
        return HttpResponse('用戶信息')
  • 全局使用(在settings裏邊設置配置文件):
    • 全局使用時的文件爲路徑字符串,如下;而在局部用時,直接添加驗證類的名字就行。

REST_FRAMEWORK = {
# 全局使用的認證類

```python
REST_FRAMEWORK = {
  # 全局使用的認證類
  "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],  #裏邊寫的類文件的位置
  # "UNAUTHENTICATED_USER":lambda :"匿名用戶"
  "UNAUTHENTICATED_USER":None, # 匿名,request.user = None
  "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
}

2. 源碼流程

  • dispatch
    • 封裝request
      • 獲取定義的認證類(全局/局部),通過列表生成時創建對象。
    • initial
      • perform_authentication
        request.user(內部循環…)
        - user
        - _authenticate(定義返回內容,有返回元組,沒有,返回個元組)

2. 權限

class MyPermission(object):

    def has_permission(self,request,view):
        if request.user.user_type != 3:
            return False
        return True
                                

class OrderView(APIView):
    """
    訂單相關業務(只有SVIP用戶有權限)
    """
    permission_classes = [MyPermission,]
    
    def get(self,request,*args,**kwargs):
        # request.user
        # request.auth
        self.dispatch
        ret = {'code':1000,'msg':None,'data':None}
        try:
            ret['data'] = ORDER_DICT
        except Exception as e:
            pass
        return JsonResponse(ret)

權限知識梳理:
同上,爲了代碼規範,最好也繼承自BasePermission

  • 類,必須繼承:BasePermission,必須實現:has_permission方法,其中還有個has_object_persion方法,用來細化到針對對象的權限判斷
    from rest_framework.permissions import BasePermission

    class SVIPPermission(BasePermission):
        message = "必須是SVIP才能訪問"
        def has_permission(self,request,view):
            if request.user.user_type != 3:
                return False
            return True
  • 返回值:
    • True, 有權訪問
    • False,無權訪問
  • 局部使用
    class UserInfoView(APIView):
        """
        訂單相關業務(普通用戶、VIP)
        """
        permission_classes = [MyPermission1, ]

        def get(self,request,*args,**kwargs):
            return HttpResponse('用戶信息')
  • 全局使用
    REST_FRAMEWORK = {
        "DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission']
    }

3.訪問頻率控制(節流/限流)

控制請求的訪問頻率,具體實現

代碼實現是拿ip來記錄,對於沒登錄的匿名用戶,我們能拿到的也只有ip,要是換ip訪問,這個控制不了

訪問的ip地址的獲得方式 : request.META.get('REMOTE_ADDR)

  • 1.類, 若繼承:BaseThrottle,要自己實現:allow_request、wait
  • 2.類, 若繼承:SimpleRateThrottle,只需實現實現:get_cache_key、其中的一個字段:scope = “Luffy”(配置文件中限制節流頻率的key)
import time
VISIT_RECORD = {}

class VisitThrottle(object):
    """60s內只能訪問3次"""

    def __init__(self):
        self.history = None

    def allow_request(self,request,view):
        # 1. 獲取用戶IP
        remote_addr = request.META.get('REMOTE_ADDR')
        ctime = time.time()
        if remote_addr not in VISIT_RECORD:
            VISIT_RECORD[remote_addr] = [ctime,]
            return True
        history = VISIT_RECORD.get(remote_addr)
        self.history = history

        while history and history[-1] < ctime - 60:
            history.pop()

        if len(history) < 3:
            history.insert(0,ctime)
            return True

        # return True    # 表示可以繼續訪問
        # return False # 表示訪問頻率太高,被限制

    def wait(self):
        """
        還需要等多少秒才能訪問
        :return:
        """
        ctime = time.time()
        return 60 - (ctime - self.history[-1])

      
class AuthView(APIView):
    """
    用於用戶登錄認證
    """
    authentication_classes = []
    permission_classes = []
    throttle_classes = [VisitThrottle,]

    def post(self,request,*args,**kwargs):
        ret = {'code':1000,'msg':None}
        try:
            user = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
            if not obj:
                ret['code'] = 1001
                ret['msg'] = "用戶名或密碼錯誤"
            # 爲登錄用戶創建token
            token = md5(user)
            # 存在就更新,不存在就創建
            models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
            ret['token'] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = '請求異常'

        return JsonResponse(ret)

源碼流程梳理

from rest_framework.throttling import BaseThrottle, SimpleRateThrottle

# 代碼中的get_ident()獲得標識,不僅可以用遠程IP,或者自己寫的標識,也可以獲得傳輸過來的代理的Ip,但是前提是請求中必須包含過來的ip數據,不然也是無法實現的

class MyThrottle(SimpleRateThrottle):
    """限制所有用戶"""
    scope = 'Luffy'
    def get_cache_key(self, request, view):
    #設置的進行頻率限制時的唯一標識。緩存的鍵,可以設置遠程ip爲標識,利用get_ident()
        return self.get_ident(request)


class UserThrottle(SimpleRateThrottle):
    """限制已經登錄用戶的訪問頻率"""
    scope = 'LuffyUser'
    def get_cache_key(self, request, view):
        #設置進行頻率限制的唯一標識。緩存的鍵,可設置遠程ip爲標識,利用get_ident()
        return request.user.username
  • 全局使用
            REST_FRAMEWORK = {
REST_FRAMEWORK = {
    # 全局使用的認證類
    "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],
    # "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication', ],
    # "UNAUTHENTICATED_USER":lambda :"匿名用戶"
    "UNAUTHENTICATED_USER":None, # 匿名,request.user = None
    "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
    "DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission'],
    # 這裏設置的是針對所有用戶頻率的限制,針對單獨的爲登錄用戶的設置可以直接在auth認證那裏
    # 直接添加字段;如果頻率一樣的話,可以直接針對ip進行全局的限制。
    "DEFAULT_THROTTLE_CLASSES":["api.utils.throttle.UserThrottle"],
    "DEFAULT_THROTTLE_RATES":{
        "Luffy":'3/m',   # 3次每分鐘(SimpleRateThrottle內部方法實現)
        "LuffyUser":'10/m',    #
    }
}
  • 局部單獨使用,直接加導視圖函數內就行throttle_classes = [UserThrottle,](見上方代碼)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章