django-rest-framework之认证系统源码解析

我们先了解一下django的调度过程,使用工具vscode,右键方法或者类,可以看到django源代码内容
在这里插入图片描述

Django如何处理请求

当用户从Django支持的站点请求页面时,系统将遵循以下算法来确定要执行的python代码:
1.Django确定要使用的根urlconf模块。通常,这是 ROOT_URLCONF 设置,但如果传入 HttpRequest 对象具有 urlconf 属性(由中间件设置),其值将用于替代 ROOT_URLCONF 设置。
2.Django加载该python模块并查找变量 urlpatterns . 这应该是一个 sequence 属于 django.urls.path() 和/或 django.urls.re_path() 实例。
3.Django按顺序运行每个URL,并在第一个匹配请求的URL的模式处停止,匹配 path_info 一旦其中一个URL模式匹配,Django导入并调用给定的视图.
4.如果没有匹配的URL模式,或者在此过程中的任何一点期间引发异常,Django将调用适当的错误处理视图。

#路由层
urlpatterns = [
    re_path('list/(.*?)/', views.SnippetList.as_view()),
    path('<int:pk>/',  views.SnippetDetail.as_view()),
    path('users/', views.UserList.as_view()),
    path('users/<int:pk>/', views.UserDetail.as_view()),
    path('api_root', views.api_root),

1.路由器接受请求时会执行视图类(Views)的as_view方法,而as_view方法中会执行dispacth方法,然后在根据请求的类型执行相应的方法(get、post等)。django rest framework中的视图类需要继承APIView,请求到达视图类会执行视图类的as_view方法

@classmethod
    def as_view(cls, **initkwargs):
        """
        将原始类存储在视图函数中。
这允许我们在执行URL时发现关于视图的信息
反向查找。用于生成面包屑。
        """
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet): # 对获取cls里面的queryset的值,进行子父类比较
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation

        view = super(APIView, cls).as_view(**initkwargs)   #执行父类as_view()方法
        view.cls = cls
        view.initkwargs = initkwargs

        #:注意:基于会话的身份验证是经CSRF显式验证的,
        # 所有其他身份验证都是CSRF豁免的。
        return csrf_exempt(view)

2.因为APIView中as_view又执行了父类的as_view方法,而APIView的父类是View类以下是View类中的as_view()的源码:
主要是 ,验证请求方法,返回视图的名称引用,调用self.dispatch方法

class View:
    '''省略一万字'''
	def as_view(cls, **initkwargs):
	        """
	       请求-响应流程的主入口点。
	        """
	
	        for key in initkwargs:
	 			if key in cls.http_method_names: #验证请求方法
	                raise TypeError("You tried to pass in the %s method name as a "
	                                "keyword argument to %s(). Don't do that."
	                                % (key, cls.__name__))
	            if not hasattr(cls, key):
	                raise TypeError("%s() received an invalid keyword %r. as_view "
	                                "only accepts arguments that are already "
	                                "attributes of the class." % (cls.__name__, key))
	
	        def view(request, *args, **kwargs):
	            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
	            #分配视图
	            return self.dispatch(request, *args, **kwargs)
	
	        view.view_class = cls
	        view.view_initkwargs = initkwargs
	
	        #从类中获取名称和文档字符串
	        update_wrapper(view, cls, updated=())
	
	        #以及decorator设置的可能属性
	        # 比如csrf_exempt from分派
	        update_wrapper(view, cls.dispatch, assigned=())
	 		#返回view函数名称引用
	        return view

其中的验证请求方法

 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

3.我们接着看view返回的dispatch方法时候会执行self.initialize_request方法,会对django原始的request进行封装。

def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch() '与Django的常规分派基本相同,但是有额外的钩子用于启动、finalize和异常处理。.
        """
        self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)  #对django原始的request进行封装,返回Request对象
        self.request = request
        self.headers = self.default_response_headers  # 反对?

        try:
            self.initial(request, *args, **kwargs)  #这里request参数实则是Request对象

            #获取适当的处理程序方法,改程小写判断是否是合法的http方法
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),  #在self.http_method_not_allowed中找到对应的方法
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

			#<Response status_code=403, "text/html; charset=utf-8">
            response = handler(request, *args, **kwargs) #返回response对象 和状态吗,文件,charset
            print(response)
    except Exception as exc:
            response = self.handle_exception(exc) #通过返回适当的响应,处理发生的任何异常,或再次抛出错误。

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

4.再看看initialize_request源码封装的内容,以下是self.initialize_request()源码:

def initialize_request(self, request, *args, **kwargs):
    """
    返回初始请求对象。
    """
    parser_context = self.get_parser_context(request)

    return Request(      #实例化Request类,
        request,         #django原始的request对象,封装到Request中变成self._request  
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),  #从这里开始就是认证流程
        negotiator=self.get_content_negotiator(),  #开始权限验证
        parser_context=parser_context
    )

5.self.initialize_request()源码分析,实例化Request()类,封装原始的request,authenticators(认证),执行self.get_authenticators(),到了这里就开始django rest framework的认证流程,以下是

#self.get_authenticators()源码:
 def get_authenticators(self):
        """
        实例化并返回此视图可以使用的身份验证器列表
        """
        return [auth() for auth in self.authentication_classes] #列表生成式返回认证类的对象列表

6.self.get_authenticators()源码分析,循环遍历self.authentication_classes,并实例化其中的每一个类返回列表。如果我们没有定义会自动使用settings中DEFAULT_AUTHENTICATION_CLASSES作为默认(全局)

class APIView(View):

    # 以下策略可以全局设置,也可以按视图设置。
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES  #默认认证配置
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES #权限认证
    content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
    metadata_class = api_settings.DEFAULT_METADATA_CLASS
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

7.继续分析APIView的dispatch方法,此时执行self.inital方法,并将封装过后的request对象(Reuqest)作为参数进行传递,下面是self.inital()方法源码:

   def initial(self, request, *args, **kwargs):
        """
       运行在调用方法处理程序之前需要发生的任何事情。.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # 执行内容协商并在请求中存储接受的信息
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # 如果正在使用版本控制,请确定API版本。
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        #确保传入的请求是允许的
        self.perform_authentication(request)  #实现认证
        self.check_permissions(request)
        self.check_throttles(request)

8.我们接着看 self.perform_authentication(request) 对传入的请求执行身份验证,并执行user方法

 def perform_authentication(self, request):
        """
        对传入的请求执行身份验证。.

        请注意,如果您覆盖了这个选项并简单地‘pass’,则是身份验证
		第一次也会被惰性地执行吗
		的请求。用户”或“请求。身份验证的访问。.
        """
        request.user

9.在self.inital方法中会执行self.perform_authentication方法,而self.perform_authentication方法用会执行request.user,是实例化后的对象

class Request(object):
    """
   包装器允许增强一个标准的“HttpRequest”实例。
	Kwargs:
	-请求(HttpRequest)。原始请求实例。
	parsers_classes(列表/元组)。的解析器
	请求的内容。
	authentication_classes(列表/元组)。用于尝试的身份验证
	验证请求的用户。
    """

    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
		#断言,在函数体为false时执行
        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      #django原生的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

我们接着看user方法

 @property
 def user(self):
        """
        返回与当前请求关联的经过身份验证的用户
		通过提供给请求的身份验证类。
        """
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate() #验证请求
        return self._user

10.从上源码分析,在Request对象中,user属性是一个属性方法,并会执行self._authentication方法,在继续看Request对象的self._authentication方法:

   def _authenticate(self):
        """
       尝试使用每个身份验证实例验证请求
		反过来。
        """
        for authenticator in self.authenticators:
            try:
				#返回值为元组,(self.force_user, self.force_token)
              	user_auth_tuple = authenticator.authenticate(self)
             #如果authenticate方法抛出异常,self._not_authenticated()执行
            except exceptions.APIException:
                self._not_authenticated()
                raise
			#返回None,表示当前认证不处理,等下一个认证来处理
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple  #返回值self.user, self.auth
                return

        self._not_authenticated()

self.authenticators = authenticators or ()属性,为真是实例化

 self.authenticators = authenticators or ()

authenticator.authenticate(self)的方法 ,一般这是我们自己写的

  def authenticate(self, request):
        return (self.force_user, self.force_token)

self.not_authenticated方法

def _not_authenticated(self):
        """
        设置身份验证器、用户和authtoken,表示未经身份验证的请求。
		默认值为None, AnonymousUser & None。
        """
        self._authenticator = None

        if api_settings.UNAUTHENTICATED_USER:
            self.user = api_settings.UNAUTHENTICATED_USER()   #匿名用户配置,默认返回AnonymousUser
        else:
            self.user = None

        if api_settings.UNAUTHENTICATED_TOKEN:
            self.auth = api_settings.UNAUTHENTICATED_TOKEN()  #None
        else:
            self.auth = None

11.流程:
异常验证逻辑:

1.如果有异常则执行self._not_authenticated()方法,继续向上抛异常。
2.如果有返回值必须是一个元组,分别赋值给self.user, self.auth(request.user和request.auth),并跳出循环。如果返回None,则由下一个循环处理,如果都为None,则执self._not_authenticated(),返回 (AnonymousUser,None)
3.当都没有返回值,就执行self._not_authenticated(),相当于匿名用户,没有通过认证,并且此时django会返回默认的匿名用户设置AnonymousUser,如需要单独设置匿名用户返回值,则编写需要写UNAUTHENTICATED_USER的返回值:

全局配置方法:
settings.py

#设置全局认证
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES":['API.utils.auth.Authentication',]   #里面写你的认证的类的路径
}

自定义认证

rest_framework里面内置了一些认证,我们自己写的认证类都要继承内置认证类 “BaseAuthentication”

class BaseAuthentication(object):
    """
  	所有身份验证类都应该扩展BaseAuthentication。.
    """

    def authenticate(self, request):
        """
       对请求进行身份验证并返回一个二元组 (user, token).
        """
        #内置的认证类,authenticate方法,如果不自己写,默认则抛出异常
        raise NotImplementedError(".authenticate() must be overridden.")

    def authenticate_header(self, request):
        """
        返回一个字符串作为“WWW-Authenticate”的值
		在“401未经验证”的回复中,或在“没有”的情况下
		认证方案应该返回“403 Permission Denied”的响应。.
        """
        #authenticate_header方法,作用是当认证失败的时候,返回的响应头
        pass

自定义:

1)创建认证类

继承BaseAuthentication —>>1.重写authenticate方法;2.authenticate_header方法直接写pass就可以(这个方法必须写)

2)authenticate()返回值(三种)

None ----->>>当前认证不管,等下一个认证来执行
raise exceptions.AuthenticationFailed(‘用户认证失败’)
有返回值元祖形式:(元素1,元素2)
#元素1复制给request.user; 元素2复制给request.auth。

注意:self.function是实例化方法,cls.function是类方法
参考 大神教程

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值