1. 程式人生 > >Django框架(二十一)—— Django rest_framework-許可權元件

Django框架(二十一)—— Django rest_framework-許可權元件

目錄

Django rest_framework-許可權元件

一、許可權元件的使用

# 使用者資訊表
class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    # 寫choice
    user_choice=((0,'普通使用者'),(1,'會員'),(2,'超級使用者'))
    # 指定choice,可以快速的通過數字,取出文字
    user_type=models.IntegerField(choices=user_choice,default=0)
    pwd = models.CharField(max_length=32)

# 使用者token
class UserToken(models.Model):
    token = models.CharField(max_length=64)
    user = models.OneToOneField(to=UserInfo)

1、使用語法

from rest_framework.permissions import BasePermission

# 寫一個許可權類
class UserPermission(BasePermission):
    # 重寫沒有許可權時的資料
    message = '您沒有許可權'
    # 重寫has_permission()方法,傳入三個引數
    # 第一個是物件自身(自動傳);第二個是request物件;第三個是
    def has_permission(self, request, view):
        # 只要認證通過,就會把當前使用者物件放到request中
        user_type = request.user.user_type
        # get_欄位名_display() 可以獲取choice數字對應的具體值
        # user_type_name = request.user.get_user_type_display()
        # print(user_type_name)
        if user_type == 2:
            return True
        return False
class Book(APIView):
    # 使用者認證
    authentication_classes = [UserAuth.UserAuth, ]
    # 許可權判斷
    permission_classes = [MyPerm.UserPermission, ]

    def get(self, request, *args, **kwargs):
        response = {'status': 100, 'msg': '查詢成功'}
        ret = models.Book.objects.all()
        ser = MySerializer.BookSerializer(instance=ret, many=True)
        response['data'] = ser.data
        return JsonResponse(response, safe=False)

2、全域性使用、區域性使用、區域性禁用許可權

(1)全域性使用

  • 在settings檔案中配置,配置完以後,就無需在檢視類中寫,已經是所有檢視類都需要許可權判斷
  • 必須為REST_FRAMEWORK,key值必須為DEFAULT_AUTHENTICATION_CLASSES
REST_FRAMEWORK={
    'DEFAULT_PERMISSION_CLASSES':['app01.MyPerm.UserPermission',],
}

(2)區域性使用

在需要使用的檢視類中寫,只對當前檢視類起認證作用,重新定義permission_classes

class Book(APIView):
    # # 該方法是區域性使用認證
    authentication_classes = [UserAuth.UserAuth, ]
    # 該方法是區域性使用許可權
    permission_classes = [MyPerm.UserPermission, ]

    def get(self, request, *args, **kwargs):
        response = {'status': 100, 'msg': '查詢成功'}
        ret = models.Book.objects.all()
        ser = MySerializer.BookSerializer(instance=ret, many=True)
        response['data'] = ser.data
        return JsonResponse(response, safe=False)

(3)區域性禁用

在配置過全域性許可權判斷以後,有些檢視類不需要判斷許可權,可以區域性禁用許可權證,只需將permission_classes定義為空列表即可。

class Book(APIView):
    # # 該方法是區域性使用認證
    authentication_classes = [UserAuth.UserAuth, ]
    # 該方法是區域性禁用許可權
    permission_classes = []

    def get(self, request, *args, **kwargs):
        response = {'status': 100, 'msg': '查詢成功'}
        ret = models.Book.objects.all()
        ser = MySerializer.BookSerializer(instance=ret, many=True)
        response['data'] = ser.data
        return JsonResponse(response, safe=False)

二、原始碼分析

as_view ----------> view -------------> dispatch -------> Request包裝新的request ------> 認證、許可權、頻率 --------> 根據請求方式分發到不同的方法

url(r'books/',views.Book.as_view())

1、Book中沒有as_view

2、APIView的as_view

class APIView(View):
    
    @classmethod
    # cls 是 Book類
    def as_view(cls, **initkwargs):
            
        # view = super(APIView, Book).as_view(**initkwargs)
        view = super(APIView, cls).as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        return csrf_exempt(view)

3、view = super(APIView, cls).as_view(**initkwargs) ---------------------> View中的as_view

class View(object):
    
    @classonlymethod
    # cls====> Book
    def as_view(cls, **initkwargs):

        def view(request, *args, **kwargs):
            # 例項化產生一個book物件
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            # 調dispatch方法
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

4、return self.dispatch(request, *args, **kwargs) ----------------> dispatch

self====> Book物件,一層層找dispatch

APIView中找到dispatch

class APIView(View):
    
    def dispatch(self, request, *args, **kwargs):

        self.args = args
        self.kwargs = kwargs
        
        # (a)初始化request,就是通過Request類來包裝原生request,得到包裝後的request
        request = self.initialize_request(request, *args, **kwargs)
        # 從現在開始request就是包裝後的request
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            # (b) 認證、許可權、頻率
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            # http_method_names表示列表['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
    
    

(a)request = self.initialize_request(request, *args, **kwargs) 包裝 request

self 是Book物件

class APIView(View):
    # 預設的認證列表類
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)
        # (a-b)例項化初始化產生新的request物件
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),  # 認證類例項化產生的物件的列表
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        return [auth() for auth in self.authentication_classes]
(a------1)return Request( ··· ) ----------> Request類初始化
    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        assert isinstance(request, HttpRequest), (
            'The `request` argument must be an instance of '
            '`django.http.HttpRequest`, not `{}.{}`.'
            .format(request.__class__.__module__, request.__class__.__name__)
        )

        self._request = request
        self.parsers = parsers or ()
        self.authenticators = authenticators or ()
        self.negotiator = negotiator or self._default_negotiator()
        self.parser_context = parser_context
        self._data = Empty
        self._files = Empty
        self._full_data = Empty
        self._content_type = Empty
        self._stream = Empty

        if self.parser_context is None:
            self.parser_context = {}
        self.parser_context['request'] = self
        self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET

        force_user = getattr(request, '_force_auth_user', None)
        force_token = getattr(request, '_force_auth_token', None)
        if force_user is not None or force_token is not None:
            forced_auth = ForcedAuthentication(force_user, force_token)
            self.authenticators = (forced_auth,)

(b)self.initial(request, *args, **kwargs) -----> 認證、許可權、頻率

    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        # (b------1) 認證
        self.perform_authentication(request)
        # (b------2)許可權
        self.check_permissions(request)
        # 頻率
        self.check_throttles(request)
(b------1) self.check_permissions(request) -------> 許可權判斷
    def check_permissions(self, request):
        """
        Check if the request should be permitted.
        Raises an appropriate exception if the request is not permitted.
        """
        # (b------1---------1) get_permissions 許可權類物件組成的列表
        for permission in self.get_permissions():
            # 重寫的就是這個has_permission()方法,判斷當前使用者是否有許可權
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
(b------1---------1) self.get_permissions() -------> 獲取許可權類物件組成的列表
def get_permissions(self):
    """
    Instantiates and returns the list of permissions that this view requires.
    """
    return [permission() for permission in self.permission_classes]