DRF认证组件基本使用与源码剖析

139 阅读3分钟

认证组件使用和源码分析

 

1、 基本使用

class order(APIView):
authentication_classes = [MyBaseAuthentication,]
def get(self, request, *args, **kwargs):
return Response('order')

 

第二种使用方法是在settings中修改REST_FRAMWORK,这里会全局引用,

REST_FRAMEWORK={

    "UNAUTHENTICATED_USER": None,



    #全局应用组件,如果局部有自定义的组件会用局部的

    "DEFAULT_AUTHENTICATION_CLASSES":['ext.MyBaseAuthentication.MyBaseAuthentication']

}

 

 

authentication_classes = [MyBaseAuthentication,]

这一句就是使用我们定义的MyBaseAuthentication.组件

from rest_framework.authentication import BaseAuthentication

from rest_framework.exceptions import AuthenticationFailed

class  MyBaseAuthentication(BaseAuthentication):

    def authenticate(self, request):

        #做用户认证

        #1读取请求的token

        #2校验合法性

        #3返回值

            #3.1返回元组    (11,22)    认证成功  request.user   request.suth

            #3.2抛出异常        认证失败

            #返回none           多个认证类

        #token = request._request.GET.get('token')

        #query_params = _request.GET

        token = request.query_params.get('token')

        if token:

            return('zsaa',token)

        else:

            # raise AuthenticationFailed('认证失败')

            #自定义返回

            raise AuthenticationFailed({'code':11451,'msg':'请求data'})

 

这里就简单的判断,传递了token就认为认证通过。

而我们定义的MyBaseAuthentication以及其他的组件

不要放在view中,全局引用会造成循环引用,未加载完就被引用了

因为首先会setting里的REST_FRAMWORK开始加载

REST_FRAMEWORK={
    "UNAUTHENTICATED_USER": None,

    #全局应用组件,如果局部有自定义的组件会用局部的
    "DEFAULT_AUTHENTICATION_CLASSES":['views.app01.MyBaseAuthentication']

    # "DEFAULT_AUTHENTICATION_CLASSES":['ext.MyBaseAuthentication.MyBaseAuthentication']
}

此时假如我们自定义的组件写在views中,我们views导入的APIView也会被加载

屏幕截图 2024-01-22 104341.png

在APIview中又会重新加载REST_FRAMWORK就造成了循环引用。(如下图)

屏幕截图 2024-01-22 104358.png

 

 

2、认证组件源码的剖析:

def dispatch(self, request, *args, **kwargs):



 self.args = args

    self.kwargs = kwargs

    request = self.initialize_request(request, *args, **kwargs)

    self.request = request

    self.headers = self.default_response_headers  # deprecate?



    try:

        self.initial(request, *args, **kwargs)



        # Get the appropriate handler method

        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

执行 as_view()时会先到dispatch中通过initialize_request封装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

    )

authenticators=self.get_authenticators()会现在我们在views里定义的类中寻找authentication_classes,如果没有则去到settings中的全局设置,并实通过auth()例化成对象,如下

def get_authenticators(self):

 return [auth() for auth in self.authentication_classes]

 

 

因此,第一步self.initialize_request()返回的request里面包含了实例化我们的组件

 

第二步是dispatch中的

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

    self.perform_authentication(request)

    self.check_permissions(request)

    self.check_throttles(request)

 

def perform_authentication(self, request):



 request.user

这里的user在Response中,使用了@property装饰器

@property

def user(self):



 if not hasattr(self, '_user'):

        with wrap_attributeerrors():

            self._authenticate()

    return self._user

 

def _authenticate(self):

 for authenticator in self.authenticators:

        try:

            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



    self._not_authenticated()

 

这里的for authenticator in self.authenticators:会先去我们定义的类里面找authentication_classes,如果没有就会使用全局的authentication_classes。如果认证不通过就raise报错,通过了就将self.user和self.auth赋值

补充: drf会按顺序执行每个认证类中的authenticate函数,返回值可以有None和认证信息,比如token,当返回值未None时,就会继续执行下一个认证类,之道有返回值或执行完所有的认证类的authenticate。