1 绪言

上一篇中讲了django rest_framework总体流程,整个流程中最关键的一步就是执行dispatch方法。在dispatch方法中,在调用了一个initial方法,所有的认证、权限检查、访问频率控制都是在这个方法中进行的。下面代码为init方法执行这三个操作的源码:

def initial(self, request, *args, **kwargs):
"""
在调用方法处理程序之前运行需要发生的任何事情(例如:认证、权限、访问频率控制)。
"""
……
self.perform_authentication(request)#执行认证
self.check_permissions(request)#检查权限
self.check_throttles(request)#频率控制

这一篇我们来分析一下django rest_framework认证部分的源码。

2 Request对象

这里为什么提到Request对象呢?因为接下来的操作跟它息息相关。我们可以把整个浏览器到服务器的请求过程看作快递运输,Request对象就是我们要运送的包裹,所经历的各个方法就犹如快递中转站或者安检口,会检查Request对象是否符合规定,如果符合规范稍作加工就放行,移送下一个中转站,如果不符合规范,就打回原籍。认证(authentication)显然就是一个安检口,而且还是第一道安检。我们先来看一看这一道安检的入口,即perform_authentication方法的源码:

def perform_authentication(self, request):

       request.user

你没有看错,perform_authentication方法在删除注释之后就只有两行代码。代码中的request就是我们要运送的主角(Request对象),是在dispatch方法中第一步操作加工之后获得的,里面封装了原生的request对象(快递包裹中真正要寄送的东西),认证类对象(寄件人身份检查方法),以及其他的一些功能函数。这里的request.user就是封装在Request中的一个方法(可不是属性变量),我们来看看user方法的源码:

def user(self):

    if not hasattr(self, '_user'):

        with wrap_attributeerrors():

            self._authenticate()

    return self._user

在user中,如果有“_user”属性,直接返回这个属性,如果没有调用“_authenticate”方法,再来看看“_authenticate”方法:

def _authenticate(self):

    # 这里的authenticators就是封装在Request对象中的认证对象(在dispatch第一步操作中封装进去的)

    #这个authenticators就犹如快递运送中的寄件人身份核验规范,而且这个规范可能不止一个

    for authenticator in self.authenticators:#遍历每一个认证实例

        try:

            # 执行认证实例中的认证方法(快递开始安检)

            user_auth_tuple = authenticator.authenticate(self)#返回一个tuple

        except exceptions.APIException:#如果安检没有通过

            self._not_authenticated()

            raise   #抛出异常

        if user_auth_tuple is not None:#如果在安检中得到了寄件人的身份信息(也就是那个tuple不为空)

            self._authenticator = authenticator

            self.user, self.auth = user_auth_tuple#在包裹(Request)中添加寄件人信息和核验信息

            return  #返回None

    self._not_authenticated()#如果认证类设置为空
        再来看看认证实例的authenticate方法,我们以TokenAuthentication类为例进行分析,其他的认证类流程大体一致。贴出TokenAuthentication类的authenticate方法源码:
def authenticate(self, request):

    #拿到认证信息(token信息)

    #形如:Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b

    auth = get_authorization_header(request).split()#得到['Token', '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b']

    #如果认证信息为空,或者第一个元素不是'Token’

    if not auth or auth[0].lower() != self.keyword.lower().encode():

        return None

    #如果拿到的认证信息不是两个元素

    if len(auth) == 1:

        msg = _('Invalid token header. No credentials provided.')

        raise exceptions.AuthenticationFailed(msg)

    elif len(auth) > 2:

        msg = _('Invalid token header. Token string should not contain spaces.')

        raise exceptions.AuthenticationFailed(msg)

    try:

        token = auth[1].decode()#对第二个元素进行解码

    except UnicodeError:

        msg = _('Invalid token header. Token string should not contain invalid characters.')

        raise exceptions.AuthenticationFailed(msg)

    return self.authenticate_credentials(token)
def authenticate_credentials(self, key):

    model = self.get_model()#token模型类

    try:

        # 根据token在数据库中进行核验

        #这一步操作就类似于对包裹中的寄件人信息与公安局的身份证号码进行比对

        #下面的token是一个token模型类,外键关联user表,可以拿到user信息

        token = model.objects.select_related('user').get(key=key)

    except model.DoesNotExist:#如果在公安局的数据库中找不到这个寄件人信息

        raise exceptions.AuthenticationFailed(_('Invalid token.'))

    if not token.user.is_active:#用户未激活

        raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))

    return (token.user, token)#返回一个tuple
        至此,认证实例里面的authenticate方法就分析完了。我们在回到_authenticate方法,如果有认证实例列表(self.authenticators)不为空就执行authenticate方法,如果认证实例列表为空或者,或者执行authenticate方法认证失败,就执行_not_authenticated方法,来看看_not_authenticated方法源码:
def _not_authenticated(self):

    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

如下是在restframework的settings.py文件中对UNAUTHENTICATED_USER和UNAUTHENTICATED_TOKEN默认设置:

# Authentication

'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',

'UNAUTHENTICATED_TOKEN': None,

这些设置可以在自己项目文件的settings.py文件中进行重新配置。_not_authenticated方法也是直接修改Request对象中的对象和token,然后返回None。_not_authenticated方法执行完之后,一个认证就完成了,如果认证实例列表还有其他元素,就继续下一个认证,认证流程是一样的,如果所有认证实例都遍历完成,那么整个认证流程就跑完了,Request对象中就包含了认证之后的认证信息。

3 自定义认证类

    上面分析的是djangorestframework自带的认证类,如果我们要自己创建认证类该怎么做呢?从上面认证类分析中介绍到一个名为authenticate的方法,这个方法在执行认证的时候回被调用,所以自定义认证类的时候,这个方法是必须创建的,另外还有一个名为authenticate_header的方法,也是必须写的。djangorestframework有一个名为BaseAuthentication的类,这是所有认证类的父类,最好继承这个类(也可以不继承,但是那两个方法必须包含)。如下代码是自定义的一个认证类:
class MyTokenAuthentication(BaseAuthentication):

    '''自定义的认证类'''

    def authenticate(self, request):

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

        success=check_token(token)

        if success:

            return

        else:

            raise AuthenticationFailed('认证失败')

    def authenticate_header(self, request):

        pass

创建好认证类之后如何用上呢,这就涉及到配置认证类了。

4 配置认证类

在视图中,如果某个类需要指定认证类,该如何进行配置呢?有两种方法——局部配置和全局配置。

局部配置是只在配置的当前视图类中生效,配置方法时在试图类中加入下面代码:

authentication_classes = [MyTokenAuthentication , ]

注意:当认证类只有一个时,列表末尾一定要加一个逗号。在局部配置了认证类之后,restframework就只会读取当前配置的认证类,忽略默认设置。

全局配置是在项目的settings.py文件中进行配置,这样的话所有视图都会执行该配置(除非该视图自定义了局部配置)。配制方法是在项目settings.py文件中REST_FRAMEWORK中加入如下代码:

REST_FRAMEWORK={

  "DEFAULT_AUTHENTICATION_CLASSES":["myapp.auth. MyTokenAuthentication ",]

}

同样的,当认证类只有一个时,列表末尾一定要加一个逗号,列表中的元素是认证类的路径。

二、django rest_framework源码之认证流程剖析的更多相关文章

  1. 三、django rest_framework源码之权限流程剖析

    1 绪言 上一篇中分析了认证部分的源码,认证后的下一个环节就是权限判定了.事实上,权限判定肯定要与认证联合使用才行,因为认证部分不会对请求进行禁止或者是允许,而只是根据请求中用户信息进行用户身份判断, ...

  2. 一、django rest_framework源码之总体流程剖析

    1 序言 有如下django代码,视图层: from django.http import HttpResponse from rest_framework.views import APIView ...

  3. 六、django rest_framework源码之解析器剖析

    1 绪论 网络传输数据只能传输字符串格式的,如果是列表.字典等数据类型,需要转换之后才能使用但是我们之前的rest_framework例子都没有转换就直接可以使用了,这是因为rest_framewor ...

  4. 四、django rest_framework源码之频率控制剖析

    1 绪言 权限判定之后的下一个环节是访问频率控制,本篇我们分析访问频率控制部分源码. 2 源码分析 访问频率控制在dispatch方法中的initial方法调用check_throttles方法开始. ...

  5. 七、django rest_framework源码之视图

    1 绪言 当大家看大这篇博文的时候,应该对Django rest_framework中的CBV有所了解了,大致来说就是通过定义类来继承APIView类,并在类中定义get.post.put.delet ...

  6. 五、django rest_framework源码之版本控制剖析

    1 绪论 Djangorest_framework的版本控制允许用户更改不同客户端之间的行为,且提供了许多不同的版本控制方案.版本控制由传入的客户端请求确定,可以基于请求URL,也可以基于请求标头. ...

  7. Django session 源码流程

    流程 Django session源码流程 首先执行的是SessionMiddleware的init方法 import_module(settings.SESSION_ENGINE) 导入了一个 dj ...

  8. Django-restframework 源码之认证组件源码分析

    Django-restframework 源码之认证组件源码分析 一 前言 之前在 Django-restframework 的流程分析博客中,把最重要的关于认证.权限和频率的方法找到了.该方法是 A ...

  9. Django对中间件的调用思想、csrf中间件详细介绍、Django settings源码剖析、Django的Auth模块

    目录 使用Django对中间件的调用思想完成自己的功能 功能要求 importlib模块介绍 功能的实现 csrf中间件详细介绍 跨站请求伪造 Django csrf中间件 form表单 ajax c ...

随机推荐

  1. JedisCluster实践

    1. Spring中运用JedisCluster http://blog.csdn.net/u010739551/article/details/52438101[spring集成 JedisClus ...

  2. jQuery精仿手机上的翻牌效果菜单

    代码简介: jQuery精仿手机上的翻牌效果菜单,很平滑的动画翻牌效果,每点击一下菜单,就会翻去一下,貌似很灵敏的动作.注意:如果预览时没看到效果,请刷新一下页面,让jquery载入就行了,在实际使用 ...

  3. Flash数据的采集方法-搜房房价走势采集

    一般来说flash中的数据是不能被现有技术很容易采集到的,但是也不能谈flash色变,要具体问题具体分析,有些flash是可以通过一些分析发现背后的数据.然后采集就变得很容易了. 具体案例:搜房房价走 ...

  4. Ubuntu使用apt-get upgrade升级时出错

    今天在按照常规的sudo apt-get update更新软件列表后,再使用sudo apt-get upgrade升级软件时,出现了以下的错误: 正在设置 linux-image-extra-4.4 ...

  5. Remove K Digits

    Given string A representative a positive integer which has N digits, remove any k digits of the numb ...

  6. Git管理本地代码(一)【转】

    转自:http://blog.csdn.net/weihan1314/article/details/8677800 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+]   安 ...

  7. ClientDataset 三层 var and out arguments must match parameter

    ​​​将Delphi升级到10.1.2后,从客户端传ClientDataset的Delta数据到服务端程序时,出现var and out arguments must match parameter错 ...

  8. SQLAlchemy-对象关系教程ORM-query

    一:对象关系教程查询 一个 Query创建对象时使用 Session的query()方法 .此函数接受一个变量数量的参数,可以是任何类和class-instrumented描述符的组合. Query返 ...

  9. 国内能用的NTP服务器及和标准源的偏差值

    中国境内可以使用的NTP服务器的IP地址,和泰福特服务器的时间偏差值,泰福特时钟服务器实时连接天线,测试前已经连接天线超过72小时 time-a.nist.gov 129.6.15.28 NIST, ...

  10. C++之构造函数的继承

    #include<iostream> usingnamespace std; classBase1 { public: Base1()=default; Base1(const strin ...