DRF認證、自定義認證和權限、自定義權限


源碼分析

"""
1)APIView的dispath(self, request, *args, **kwargs)
2)dispath方法內 self.initial(request, *args, **kwargs) 進入三大認證
    # 認證組件:校驗用戶 - 游客、合法用戶、非法用戶
    # 游客:代表校驗通過,直接進入下一步校驗(權限校驗)
    # 合法用戶:代表校驗通過,將用戶存儲在request.user中,再進入下一步校驗(權限校驗)
    # 非法用戶:代表校驗失敗,拋出異常,返回403權限異常結果
    self.perform_authentication(request)
    
    # 權限組件:校驗用戶權限 - 必須登錄、所有用戶、登錄讀寫游客只讀、自定義用戶角色
    # 認證通過:可以進入下一步校驗(頻率認證)
    # 認證失敗:拋出異常,返回403權限異常結果
    self.check_permissions(request)
    
    # 頻率組件:限制視圖接口被訪問的頻率次數 - 限制的條件(IP、id、唯一鍵)、頻率周期時間(s、m、h)、頻率的次數(3/s)
    # 沒有達到限次:正常訪問接口
    # 達到限次:限制時間內不能訪問,限制時間達到后,可以重新訪問
    self.check_throttles(request)
    
    
3) 認證組件
    Request類的 方法屬性 user 的get方法 => self._authenticate() 完成認證
    
    認證的細則:
    # 做認證
    def _authenticate(self):
        # 遍歷拿到一個個認證器,進行認證
        # self.authenticators配置的一堆認證類產生的認證類對象組成的 list
        for authenticator in self.authenticators:
            try:
                # 認證器(對象)調用認證方法authenticate(認證類對象self, request請求對象)
                # 返回值:登陸的用戶與認證的信息組成的 tuple
                # 該方法被try包裹,代表該方法會拋異常,拋異常就代表認證失敗
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise

            # 返回值的處理
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                # 如何有返回值,就將 登陸用戶 與 登陸認證 分別保存到 request.user、request.auth
                self.user, self.auth = user_auth_tuple
                return
        # 如果返回值user_auth_tuple為空,代表認證通過,但是沒有 登陸用戶 與 登陸認證信息,代表游客
        self._not_authenticated()

4) 權限組件
    self.check_permissions(request)
    認證細則:
    def check_permissions(self, request):
        # 遍歷權限對象列表得到一個個權限對象(權限器),進行權限認證
        for permission in self.get_permissions():
            # 權限類一定有一個has_permission權限方法,用來做權限認證的
            # 參數:權限對象self、請求對象request、視圖類對象
            # 返回值:有權限返回True,無權限返回False
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
"""

自定義認證類

"""
1) 創建繼承BaseAuthentication的認證類
2) 實現authenticate方法
3) 實現體根據認證規則 確定游客、非法用戶、合法用戶
4) 進行全局或局部配置

認證規則
i.沒有認證信息返回None(游客)
ii.有認證信息認證失敗拋異常(非法用戶)
iii.有認證信息認證成功返回用戶與認證信息元組(合法用戶)
"""
utils/authentications.py
# 自定義認證類

# 1)繼承BaseAuthentication類
# 2)重新authenticate(self, request)方法,自定義認證規則
# 3)認證規則基於的條件:
#       沒有認證信息返回None(游客)
#       有認證信息認證失敗拋異常(非法用戶)
#       有認證信息認證成功返回用戶與認證信息元組(合法用戶)
# 4)完全視圖類的全局(settings文件中)或局部(確切的視圖類)配置
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from . import models
class MyAuthentication(BaseAuthentication):
    """
    同前台請求頭拿認證信息auth(獲取認證的字段要與前台約定)
    沒有auth是游客,返回None
    有auth進行校驗
        失敗是非法用戶,拋出異常
        成功是合法用戶,返回 (用戶, 認證信息)
    """
    def authenticate(self, request):
        # 前台在請求頭攜帶認證信息,
        #       且默認規范用 Authorization 字段攜帶認證信息,
        #       后台固定在請求對象的META字段中 HTTP_AUTHORIZATION 獲取
        auth = request.META.get('HTTP_AUTHORIZATION', None)

        # 處理游客
        if auth is None:
            return None

        # 設置一下認證字段小規則(兩段式):"auth 認證字符串"
        auth_list = auth.split()

        # 校驗合法還是非法用戶
        if not (len(auth_list) == 2 and auth_list[0].lower() == 'auth'):
            raise AuthenticationFailed('認證信息有誤,非法用戶')

        # 合法的用戶還需要從auth_list[1]中解析出來
        # 注:假設一種情況,信息為abc.123.xyz,就可以解析出admin用戶;實際開發,該邏輯一定是校驗用戶的正常邏輯
        if auth_list[1] != 'abc.123.xyz':  # 校驗失敗
            raise AuthenticationFailed('用戶校驗失敗,非法用戶')

        user = models.User.objects.filter(username='admin').first()

        if not user:
            raise AuthenticationFailed('用戶數據有誤,非法用戶')
        return (user, None)

系統權限類

"""
1)AllowAny:
    認證規則全部返還True:return True
        游客與登陸用戶都有所有權限

2) IsAuthenticated:
    認證規則必須有登陸的合法用戶:return bool(request.user and request.user.is_authenticated)
        游客沒有任何權限,登陸用戶才有權限
    
3) IsAdminUser:
    認證規則必須是后台管理用戶:return bool(request.user and request.user.is_staff)
        游客沒有任何權限,登陸用戶才有權限

4) IsAuthenticatedOrReadOnly
    認證規則必須是只讀請求或是合法用戶:
        return bool(
            request.method in SAFE_METHODS or
            request.user and
            request.user.is_authenticated
        )
        游客只讀,合法用戶無限制
"""

# api/views.py
from rest_framework.permissions import IsAuthenticated
class TestAuthenticatedAPIView(APIView):
    permission_classes = [IsAuthenticated]
    def get(self, request, *args, **kwargs):
        return APIResponse(0, 'test 登錄才能訪問的接口 ok')
    
    
# 因為默認全局配置的權限類是AllowAny
# settings.py
REST_FRAMEWORK = {
    # 權限類配置
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ],
}
    

自定義權限類

"""
1) 創建繼承BasePermission的權限類
2) 實現has_permission方法
3) 實現體根據權限規則 確定有無權限
4) 進行全局或局部配置

認證規則
i.滿足設置的用戶條件,代表有權限,返回True
ii.不滿足設置的用戶條件,代表有權限,返回False
"""
# utils/permissions.py
from rest_framework.permissions import BasePermission
from django.contrib.auth.models import Group
class MyPermission(BasePermission):
    def has_permission(self, request, view):
        # 只讀接口判斷
        r1 = request.method in ('GET', 'HEAD', 'OPTIONS')
        # group為有權限的分組
        group = Group.objects.filter(name='管理員').first()
        # groups為當前用戶所屬的所有分組
        groups = request.user.groups.all()
        r2 = group and groups
        r3 = group in groups
        # 讀接口大家都有權限,寫接口必須為指定分組下的登陸用戶
        return r1 or (r2 and r3)
    
    
# 游客只讀,登錄用戶只讀,只有登錄用戶屬於 管理員 分組,才可以增刪改
from utils.permissions import MyPermission
class TestAdminOrReadOnlyAPIView(APIView):
    permission_classes = [MyPermission]
    # 所有用戶都可以訪問
    def get(self, request, *args, **kwargs):
        return APIResponse(0, '自定義讀 OK')
    # 必須是 自定義“管理員”分組 下的用戶
    def post(self, request, *args, **kwargs):
        return APIResponse(0, '自定義寫 OK')

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM