DRF-源码解析-3-权限流程:drf的权限源码解析,drf的权限流程。

2024-01-08 12:43:54

在这里插入图片描述

一、代码的准备

视图:

class TestAPIView(APIView):
    authentication_classes=[MyJWTAuthentication]
    permission_classes = [AdminPermission,]
    def get(self,request)
        return Respponse({'code':200,'msg':'测试通过'})

路由:

path('test/',views.TestAPIView.as_view())

请求方式:GET

认证的配置:

#1、全局配置:settings.py
REST_FRAMEWORK = {
    #1、全局认证
    'DEFAULT_AUTHENTICATION_CLASSES':['authen.authentication.MyJWTAuthentication'],
    #2、全局的权限
    'DEFAULT_PERMISSION_CLASSES':['authen.permission.AdminPermission']
}

#2、局部配置:视图类的类变量
class TestAPIView(APIView):
	authentication_classes=[MyJWTAuthentication]
    permission_classes = [AdminPermission]

token:

import jwt
from django.conf import settings
import time


def create_token(user_id: int, timeout=None):
    '''
    :param user_id: 传递用户的id
    :param timeout: token有效时间,默认是一天
    :return:
    '''
    has_exp = hasattr(settings,'JWT_EXPIRATION_DELTA')
    if has_exp:
        jwt_exp = getattr(settings,'JWT_EXPIRATION_DELTA')
    else:
        jwt_exp = 24*60*60
    if timeout == None:
        #没有指定过期时间,就使用配置的时间戳
        timeout = jwt_exp
    elif type(timeout) != int:
        #传递的类型有误,使用配置中的时间戳
        timeout = jwt_exp

    payload = {'user_id': user_id}
    salt = settings.SECRET_KEY  # 加密的盐
    # 构造header
    headers = {
        'type': 'jwt',
        'alg': 'HS256'
    }

    now_datetime = time.time()
    payload['exp'] = now_datetime + timeout  # token过期时间,时间戳
    # print(payload)
    token = jwt.encode(payload=payload, key=salt, algorithm="HS256", headers=headers).decode('utf-8')
    return token

认证类的书写:

from rest_framework.authentication import BaseAuthentication
class MyJWTAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 获取token
        token = request.META.get("HTTP_TOKNE")  # 从请求头中获取
        if not token:
            token = request.COOKIES.get('token')  # 从cookies中获取token
        if not token:
            msg = '没有携带token'
            raise AuthenticationFailed({'code':410,'error':'没有携带token'})
        '''
        1、切割
        2、解密第二段/判断过期
        3、验证第三段合法性
        '''
        # 导入settings中的字符串做盐
        salt = settings.SECRET_KEY
        payload = None
        try:
            payload = jwt.decode(token, salt, True)
            #获取用户信息
            user = models.UserModel.objects.filter(id=payload.get('user_id'),is_enable=True).first()
            if user:
                return (user, token)
                # request.user,request.auth
            else:
                raise AuthenticationFailed({'code':410,'error':'token有问题'})
        except exceptions:
            msg = 'token有问题,请重新登录'
            raise AuthenticationFailed({'code': 410, 'error': msg})

权限类书写:

from rest_framework.permissions import BasePermission
from rest_framework import exceptions
from django.contrib.auth.models import AnonymousUser
class  AdminPermission(BasePermission):
    message='当前用户没有管理员权限'

    def has_permission(self,request,view):
        #view是视图类的对象,拿到视图类的东西
        #拿到token解析出来的用户的权限
        user = request.user
        if type(user) == AnonymousUser or user==None:
            self.message = '没有携带token'
            return False

        if user.role == 1:
            #权限是1,管理员时,才能访问接口
            return True
        else:
            #其他权限无法访问
            return False

二、具体的流程

1、TestAPIView的as_view方法,当前实例对象没有,找父类的APIView的as_view

@classmethod
def as_view(cls, **initkwargs):
    #1、调用APIView父类的as_view方法
    view = super().as_view(**initkwargs)
    view.cls = cls
    view.initkwargs = initkwargs
    #2、去除掉csrf校验
    return csrf_exempt(view)

2、APIView的as_view是调用父类View的as_view方法

@classonlymethod
def as_view(cls, **initkwargs):
    def view(request, *args, **kwargs):
        self = cls(**initkwargs)
        self.setup(request, *args, **kwargs)
        if not hasattr(self, 'request'):
            raise AttributeError(
                "%s instance has no 'request' attribute. Did you override "
                "setup() and forget to call super()?" % cls.__name__
            )
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs
    return view
 
#返回的是闭包view,
#view的功能就是self.dispatch()方法

3、as_view主要返回闭包,关于self.dispatch()方法

4、self是TestAPIView , 没有dispatch方法,调用父类APIView的dispatch方法

#简化后的源码

    def dispatch(self, request, *args, **kwargs):
        #处理url中的参数
        self.args = args
        self.kwargs = kwargs
        #构建drf的request对象
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            #认证、权限、限流的执行
            self.initial(request, *args, **kwargs)

            #通过反射来执行TestAPIView中的get、post等方法
            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

5、self.initialize_request,TestAPIView没有,调用APIView的initialize_request方法,构建drf的request对象

    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),#获取认证对象
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        ) 

5.1、self.get_authenticators(),TestAPIView没有,调用父类的APIView的get_authenticators

    def get_authenticators(self):
        return [auth() for auth in self.authentication_classes]

5.2、 self.authentication_classes : self=TestAPIView的实例对象

  • 如果TestAPIView中设置了authentication_classes 成员变量

    • class TestAPIView(APIView):
          authentication_classes=[MyJWTAuthentication]
          def get(self,request)
              return Respponse({'code':200,'msg':'测试通过'})
      
  • 如果TestAPIView没有设置,就去父类APIView中获取

    • class APIView(View):
          authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
      
      #这里就是去settings.py中获取配置
      REST_FRAMEWORK = {
          #1、全局认证
          'DEFAULT_AUTHENTICATION_CLASSES':['authen.authentication.MyJWTAuthentication'],
      }
      

5.3、获取到的就是[认证类(),]

5.4、新的request的初始化:drf的request对象,其实例变量authenticators=[认证类(),]

class Request:
    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        self._request = request
        self.authenticators = authenticators or ()

6、self.initial(request,),TestAPIView中没有,找APIView的initial方法,

    def initial(self, request, *args, **kwargs):
        #认证、权限、限流
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)

7、self.perform_authentication(request), TestAPIView中没有,找APIView的perform_authentication方法

    #request 是drf的request,调用request的user
    def perform_authentication(self, request):
        request.user

8、request是drf创建的request对象,调用request.user方法(此后,就是去找Request类了)

from rest_framework.request import Request

@property
def user(self):
    if not hasattr(self, '_user'):
        with wrap_attributeerrors():
        	self._authenticate()
    return self._user
  • 在实例化request对象时,初始化中没有_user的成员变量

  • 执行self._authenticate()方法,request的self._authenticate()方法

    •     #self=request
          def _authenticate(self):
              #去Request的authenticators 实例变量中获取认证类对象类别
              for authenticator in self.authenticators:
                  try:
                      #执行authenticate方法,返回元组,(user,token)
                      user_auth_tuple = authenticator.authenticate(self)
                  except exceptions.APIException:
                      self._not_authenticated()
                      raise
      			#认证通过
                  if user_auth_tuple is not None:
                      self._authenticator = authenticator
                      self.user, self.auth = user_auth_tuple
                      return
      		#认证不通过时,就是给request.user,request.auth 设置匿名用户了
              self._not_authenticated()
      

9、认证时,token没有异常:就进入下一步了

    def initial(self, request, *args, **kwargs):
        # Ensure that the incoming request is permitted
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)

10、self.check_permissions(request) 方法,TestAPIView没有,找父类APIView的check_permissions方法

    def check_permissions(self, request):
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                #抛出异常错误
  • self.get_permissions(),TestAPIView没有,找APIView的get_permissions()

    •     def get_permissions(self):
              return [permission() for permission in self.permission_classes]
      
    • self.permission_classes

      • 如果TestAPIView中配置了permission_classes,优先使用视图函数中的
      • 如果TestAPIView没有配置,就使用APIView的
    • 结果:返回[权限对象1,权限对象2,…]

  • 循环遍历[权限对象1,权限对象2,…], 执行权限对象.has_permission方法

    • 要所有的权限对象都return True,都满足,才能进入到下一步
    • 只要其中一个不满足,就报错

11、假设所有权限对象都通过了

    def dispatch(self, request, *args, **kwargs):
        #处理url中的参数
        self.args = args
        self.kwargs = kwargs
        #构建drf的request对象
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            #认证、权限、限流的执行
            self.initial(request, *args, **kwargs)

            #通过反射来执行TestAPIView中的get、post等方法
            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

12、使用反射,TestAPIView的get方法,执行后,获取返回值。

总结:
1、权限类是依赖于认证类的完成,没有设置认证的视图,就不应该设置权限类
2、权限类的流程,大致跟认证差不多的

文章来源:https://blog.csdn.net/weixin_46371752/article/details/135453382
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。