DRF-Permission元件原始碼分析及改編原始碼

harry6發表於2024-10-27

1. 許可權元件原始碼分析

PS:下列原始碼為了方便理解都進行了簡化,只保留了許可權相關的程式碼


由於檢視函式中繼承了APIView,因此permission_classes可在檢視類中進行重寫。

注意點:
  • 執行許可權校驗前,已執行了認證流程。因此此時可透過self.user獲取使用者物件(認證透過的情況)
    image



2. 實踐:編寫一個許可權類

假設我們在認證透過後,給每個使用者物件都加上表示角色的屬性role。1代表普通使用者, 2代表經理, 3代表BOSS:

import random
from rest_framework.permissions import BasePermission
from rest_framework.request import Request

class UserPermission(BasePermission):
    message = {"code": 1001, "detail": "無許可權"}  # 無許可權時返回的資訊

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

class ManagerPermission(BasePermission):
    message = {"code": 1001, "detail": "無許可權"}  # 無許可權時返回的資訊

    def has_permission(self, request, view):
        if request.user.role == 2:
            return True
        return False

class BossPermission(BasePermission):
    message = {"code": 1001, "detail": "無許可權"}   # 無許可權時返回的資訊

    def has_permission(self, request, view):
        if request.user.role == 1:
            return True
        return False



3. 原始碼改編

  • 將許可權校驗中的“且”改為“或”關係,即只要有一個許可權類透過校驗即代表許可權校驗透過,繼續執行後續程式碼:
# 在檢視類中重寫原始碼中的check_permissions方法:
    def check_permissions(self, request):
        no_permission_objects = []  # 未透過校驗的許可權物件
        for permission in self.get_permissions():
            if permission.has_permission(request, self):
                # 只要有一個許可權類透過校驗則立即停止函式
                return 
            else:
                no_permission_objects.append(permission)
        else:   # 所有許可權類都未透過校驗
            self.permission_denied(
                request,
                message=getattr(no_permission_objects[0], 'message', None),  
                code=getattr(no_permission_objects[0], 'code', None))
  • 若想要所有檢視類能使用該重寫的功能,可將該方法單獨寫成類(MyAPIView),並繼承APIView;其他要使用該重寫方法的檢視不再繼承APIView,而是繼承MyAPIView
# utils.py
class MyAPIView(APIView):
    # 改寫許可權,變為"或"關係;
    def check_permissions(self, request):
        no_permission_objects = []  # 未透過校驗的許可權物件
        for permission in self.get_permissions():
            if permission.has_permission(request, self):
                # 只要有一個許可權類透過校驗則立即停止函式
                return 
            else:
                no_permission_objects.append(permission)
        else:   # 所有許可權類都未透過校驗
            self.permission_denied(
                request,
                message=getattr(no_permission_objects[0], 'message', None),  
                code=getattr(no_permission_objects[0], 'code', None))

# views.py
class UserView(APIView):  # 繼承DRF的APIView,則許可權判斷為“且”的關係
    # 只有使用者有許可權
    permission_classes = [UserPermission]

    def get(self, request, pid):
        return Response("hello get")

    def post(self, request, pid):
        return Response({"nihao  post!"})


class OrderView(MyAPIView):  # 用了自己改寫的類MyAPIView,則許可權判斷為“或"的關係
    # 經理和BOSS都有許可權
    permission_classes = [BossPermission, ManagerPermission]

    def get(self, request, pid):
        return Response("hello get")

    def post(self, request, pid):
        return Response({"nihao  post!"})

相關文章