美文网首页
认证源码流程

认证源码流程

作者: 小吉头 | 来源:发表于2020-07-21 10:06 被阅读0次

认证模块

rest_framework.authentication是restframework提供的认证模块,其他认证类都要继承BaseAuthentication类。

分析认证源码流程

from rest_framework.views import APIView


class IndexView(APIView):

    def get(self,request):
        return HttpResponse("success")

APIView继承自django的View(from django.views import View)
根据CBV流程,最终会调用self.dispatch(request, *args, **kwargs),self指向IndexView示例,先去IndexView查找dispatch(),没有再去APIView查找,APIView重写了dispatch(),增加了部分属性:

 def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        #封装新的Request对象
        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

封装新的Request对象

把原来的request作为参数传递给了initialize_request(...)
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self指向IndexView实例,先去IndexView类查找,未找到再去父类查找,在父类找到initialize_request(...)方法,源码如下:

class APIView(View):
    ...
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES#如果在django项目settings.py中定义了`REST_FRAMEWORK`变量,会优先从那里读取
    ...

    def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        return [auth() for auth in self.authentication_classes] #这里self指向IndexView实例,先去IndexView中查找,如果未定义再去父类查找

    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
        )

调用了Request类,重新封装了原来的request,并且增加了authenticators属性,本篇只看认证,其他先忽略。
本例在settings.py中定义了认证类:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ],
}

所以authenticators = [BasicAuthentication(),SessionAuthentication()]

如何从新的Request对象中获取请求参数

Request类定义:from rest_framework.request import Request

class IndexView(APIView):

    def get(self,request):
        print(request._request.GET.get("user")) #直接使用原生的request对象
        print(request.query_params.get("user"))
        print(request.GET.get("user"))
        return HttpResponse("success")

request.query_params.get("user")查看Request类中的定义:

    @property
    def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET

print(request.GET.get("user"))为什么封装后的request对象也能直接调用GET属性获取,因为重写了内置方法__getattr__()

    def __getattr__(self, attr):
        """
        If an attribute does not exist on this instance, then we also attempt
        to proxy it to the underlying HttpRequest object.
        """
        try:
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)

验证是否认证

self.initial(request, *args, **kwargs)这里的request是封装后的Request实例对象,查看源码发现验证认证的代码是self.perform_authentication(request)
对应的源码:

    def perform_authentication(self, request):
        """
        Perform authentication on the incoming request.

        Note that if you override this and simply 'pass', then authentication
        will instead be performed lazily, the first time either
        `request.user` or `request.auth` is accessed.
        """
        request.user

在Request类中查找user属性的实现代码:

    @property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()
        return self._user


    def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        for authenticator in self.authenticators:
            try:
                user_auth_tuple = authenticator.authenticate(self)#authenticate()可以有三种结果1、返回元祖包含两个元素作为self.user和self.auth赋值 2、返回None 3、可以抛异常
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:#遍历self.authenticators,如果返回None就去遍历下一个对象,如果得到tuple赋值后就返回
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return

        self._not_authenticated()

     def _not_authenticated(self):#匿名用户self.user和self.auth赋值
            """
             Set authenticator, user & authtoken representing an unauthenticated request.

             Defaults are None, AnonymousUser & None.
             """
             self._authenticator = None

             if api_settings.UNAUTHENTICATED_USER:#可以在settings.py定义UNAUTHENTICATED_USER变量,指向一个函数或者直接为None
                 self.user = api_settings.UNAUTHENTICATED_USER()
             else:
                 self.user = None

             if api_settings.UNAUTHENTICATED_TOKEN:#可以在settings.py定义UNAUTHENTICATED_TOKEN变量,指向一个函数或者直接为None
                 self.auth = api_settings.UNAUTHENTICATED_TOKEN()
             else:
                 self.auth = None

for authenticator in self.authenticators:这里的self.authenticators就是 刚才分析得到的[BasicAuthentication(),SessionAuthentication()]

遍历list中对象,调用authenticator.authenticate(self)进行认证,这里self指的是封装的Request实例对象
BasicAuthentication为例,查看源码:

from rest_framework.authentication import BasicAuthentication,SessionAuthentication
class BaseAuthentication:
    """
    All authentication classes should extend BaseAuthentication.
    """

    def authenticate(self, request):
        """
        Authenticate the request and return a two-tuple of (user, token).
        """
        raise NotImplementedError(".authenticate() must be overridden.")

    def authenticate_header(self, request):
        """
        Return a string to be used as the value of the `WWW-Authenticate`
        header in a `401 Unauthenticated` response, or `None` if the
        authentication scheme should return `403 Permission Denied` responses.
        """
        pass


class BasicAuthentication(BaseAuthentication):
     """
    HTTP Basic authentication against username/password.
    """
    www_authenticate_realm = 'api'
    def authenticate(self, request):...

    def authenticate_credentials(self, userid, password, request=None):...

    def authenticate_header(self, request):
        return 'Basic realm="%s"' % self.www_authenticate_realm

BaseAuthentication作为内置认证类的父类,提供了authenticate()authenticate_header(),子类必须实现authenticate()方法

自定义认证类

根据验证认证的规则,自定义认证类,继承自BaseAuthentication
utils目录下新建auth.py

from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication


class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        if request._request.GET.get("user"):
            return ('xiaobai','test token')
        else:
            raise exceptions.AuthenticationFailed("用户认证失败")

    def authenticate_header(self, request):
        pass

views.py

from django.http import HttpResponse
from rest_framework.views import APIView

from utils.auth import MyAuthentication


class IndexView(APIView):
    authentication_classes = [MyAuthentication]

    def get(self,request):
        print(request.user)
        print(request.auth)
        return HttpResponse("success")
>>>xiaobai
>>>test token

全局配置

如果想让所有视图都必须要认证后才能访问,可以在django项目的settings.py中设置REST_FRAMEWORK变量,即本篇开始时写的,修改成自定义的认证类:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ['utils.auth.MyAuthentication'],
    'UNAUTHENTICATED_USER':None,#_not_authenticated()函数内部调用,如果没有覆盖使用rest_framework settings里面默认的。覆盖后结果request.user = None
    'UNAUTHENTICATED_TOKEN':None#_not_authenticated()函数内部调用,如果没有覆盖使用rest_framework settings里面默认的。覆盖后结果request.auth = None
}

局部配置

如果只是某个视图使用,可以在类中定义属性,比如:authentication_classes = [MyAuthentication]
如果不需要认证,可以写成authentication_classes = []

相关文章

网友评论

      本文标题:认证源码流程

      本文链接:https://www.haomeiwen.com/subject/oaxvqktx.html