zoukankan      html  css  js  c++  java
  • Django Rest Framework之认证

    代码基本结构

      url.py:

    from django.conf.urls import url, include
    from web.views.s1_api import TestView
     
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    

       views.py:

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.request import Request
    from rest_framework import exceptions
    
    
    class TestAuthentication(BaseAuthentication):
        def authenticate(self, request):
        '''
        认证代码编写区域
        '''
        return (用户,用户Token)
    
        def authenticate_header(self, request):
            # 验证失败时,返回的响应头WWW-Authenticate对应的值
            pass  
    
    
    class TestView(APIView):
        authentication_classes = [TestAuthentication, ]  
    
        def get(self, request, *args, **kwargs):
            pass
    
        
         def post(self, request, *args, **kwargs):
            pass
    
            '''
            等等一系列的视图功能方法
            '''

       说明:

        1)在authenticate方法的返回值是一个元组,元组中第一个元素是用户名,第二个元素是认证数据token。这个返回值会在我们的视图类中通过request.user 和 request.auth获取到。具体为什么是这两个值,会在后面的源码分析中说明。

        2)认证的返回值有三种情况:

          返回元组(如(1)中所述):认证成功

          返回None:处理下一个认证类

          抛出异常:认证失败

        3)上面的基本结构是做局部的类的认证方式,如果相对绝大多数的类做认证,那么可以通过全局认证的方式实现。该方法在下文中介绍。

        4)authentication_classes 属性变量是一个列表,列表元素是类,一般情况只使用一个认证类。     

    源码分析

      1) 为什么要使用authentication_classes 属性变量?

      

       python 的面向对象编程中,我们首先要执行的方法肯定是dispatch方法,所以我们的分析入口就是dispatch方法,在dispatch方法中,可以看到,通过initialize_request方法将django原生的request进行了一次封装。由initialize_request方法的实现过程可以看出,将其封装实例化成了一个Request对象。而authenticators属性就是认证属性。

     

       

      通过查看get_authenticators方法,可以知道,它的返回值是一个列表生成式,而这个列表生成式中所用的就是我们在认证类中赋值authenticatin_classes属性变量。在查找该变量的定义位置,就看到了它是通过settings配置文件来实现赋值的,除非,在子类中将其赋值。我们的代码就是这样做的。同时,也可以看出,我们可以修改settings配置文件来为全局定义认证规则。

    2)为什么要认证类中要使用authenticate方法?

       

       

      回到前面说是的dispatch方法来,在做完了对django原生的request的封装和实例化后,紧接着就会开始认证(try...中,捕获异常,如果没有捕获到异常,说明认证成功,就会继续执行下面的反射过程)。认证的过程就包含在上图中的inital方法中,有图可知,是通过perform_authentication方法实现的认证。

       

       

      在perform_authentication方法中可以看到,只调用了一个request.user,而这个user一定是方法,不会是属性变量,因为如果是属性变量,那么就一定有语法错误,变量一定是要赋值的,不可能孤零零的写到那里。我们在源码中找到它,就明白了,之所以它能这么写,就是因为有了property装饰器。在user方法中找到_authenticate方法,这就是认证的方法。

     

       在这个方法中,一切答案都就找到了。首先看authenticators,是不是很眼熟,没错它就是前面说的,封装和实例化原生request的Request类中所定义的属性变量。在实例化时,我们就将authentication_classes列表的值通过get_authenticators方法中的列表生成式赋值给了authenticators。再往下看,authenticator.autheneicate(self)中的authenticator是不是就是我们自己定义的认证类,而它在源码中要做“.authenticate(self)”的操作,那自然而然,我们定义的认证类中要实现这个方法了。

      3)为什么认证成功后的返回值在request.user和request.auth中?

      由 2)中最后一个图可知,当我们认证成功后会执行“self.user, self.auth = user_auth_tuple”代码,我们在认证类定义的方法authenticate的返回值就保存在 user_auth_tuple中,所以我们通过request.user 和 request.auth 就可以获取到了。

    实例

    from django.conf.urls import url, include
    from web.viewsimport TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.request import Request
    from rest_framework import exceptions
    
    token_list = [
        'sfsfss123kuf3j123',
        'asijnfowerkkf9812',
    ]
    
    
    class TestAuthentication(BaseAuthentication):
        def authenticate(self, request):
            """
            用户认证,如果验证成功后返回元组: (用户,用户Token)
            :param request: 
            :return: 
                None,表示跳过该验证;
                    如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                    self._authenticator = None
                    if api_settings.UNAUTHENTICATED_USER:
                        self.user = api_settings.UNAUTHENTICATED_USER()
                    else:
                        self.user = None
            
                    if api_settings.UNAUTHENTICATED_TOKEN:
                        self.auth = api_settings.UNAUTHENTICATED_TOKEN()
                    else:
                        self.auth = None
                (user,token)表示验证通过并设置用户名和Token;
                AuthenticationFailed异常
            """
            val = request.query_params.get('token')
            if val not in token_list:
                raise exceptions.AuthenticationFailed("用户认证失败")
    
            return ('登录用户', '用户token')
    
        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.
            """
            # 验证失败时,返回的响应头WWW-Authenticate对应的值
            pass
    
    
    class TestView(APIView):
        authentication_classes = [TestAuthentication, ]
        permission_classes = []
    
        def get(self, request, *args, **kwargs):
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    Views.py

    扩展:全局认证

      如果要进行全局配置,由上面的源码分析可知,我们只需要在配置文件中配置我们存储到authentication_classes的值即可。但还要注意的是,在写配置文件时,要使用的是路径,所以最好在和views.py同级目录下新建一个文件夹(我习惯叫utils),再在该文件夹下新建一个认证文件(auth.py),将我们的认证类都写到这里。

    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES" :['api.utils.auth.MyAuthentication]     
    }

        MyAuthentication类就是我们写在utils文件夹下auth.py文件中的认证类。

      注意:如果有部分类不需要认证的话,可以在这里类中添加“authentication_classes = []”,即可。

  • 相关阅读:
    python数据分析常用包之Matplotlib
    python数据分析常用包之Scipy
    python数据分析常用包之Pandas
    python数据分析常用包之numpy
    常见算法之‘插入类排序’
    CRM管理系统流程.king_admin
    常见算法之‘选择排序’
    常见的算法排序之交换类排序
    常用正则表达式
    scss在ide的命令参数
  • 原文地址:https://www.cnblogs.com/cjaaron/p/10433325.html
Copyright © 2011-2022 走看看