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. SQL Server 数据库备份失败解决方法

    问题:System.Data.SqlClient.SqlError: 无法使用备份文件 'D:\20160512.bak',因为原先格式化该文件时所用扇区大小为 512,而目前所在设备的扇区大小为 4 ...

  2. 20155236 2016-2017-2 《Java程序设计》第四周学习总结

    20155236 2016-2017-2 <Java程序设计>第四周学习总结 教材学习内容总结 1.继承基本上就是避免多个类间重复定义共同行为. 继承的三个好处:减少代码冗余:维护变得简单 ...

  3. Ubuntu 13.04 主机名的修改

    由于某些原因,要修改Ubuntu的主机名,晚上Google了一下,要改的地方为/etc/hostname,即将里面的字符串替换为你要起的主机名即可. sudo vi /etc/hostname 修改即 ...

  4. hive笔记之row_number、rank、dense_rank

    hive中有三个与分组排序相关的分析函数(我起初也认为是窗口函数,后来看到手册里是把他们划到了Analytics functions下),row_number.rank.dense_rank,我一直傻 ...

  5. 关于Java的“找不到或无法加载主类”

    Java编程思想4th第六章的关于访问权限和包的笔记总结时遇到了一个关于package命名及导入的问题. 环境:Ubuntu 16.04.3 LTS x86_64 首先,我要安装部署Java的开发环境 ...

  6. 配置replica set的常见问题

    总有人问起配置ReplicaSet不成功,总结了一下基本上的可能性就几种,检查步骤如下: 假设三台机器的IP分别是 A: 192.168.1.2 a.test.com B:192.168.1.3 b. ...

  7. 洛谷 P4248: bzoj 3238: [AHOI2013]差异

    题目传送门:洛谷 P4248. 题意简述: 定义两个字符串 \(S\) 和 \(T\) 的差异 \(\operatorname{diff}(S,T)\) 为这两个串的长度之和减去两倍的这两个串的最长公 ...

  8. JDOM生成XML文档的一般方法

    由于DOM提供的生成XML的方法不够直观,而且要用到各种繁琐的注解,鉴于此可借助第三方库-----JDOM生成XML文档.具体操作方式如下: import java.io.FileOutputStre ...

  9. Oracle基础结构认知—初识oracle【转】

    Oracle服务器(oracle server)由实例和数据库组成.其中,实例就是所谓的关系型数据库管理系统(Relational Database Management System,RDBMS), ...

  10. APUE-文件和目录(八)文件时间

    文件的时间 与文件相关的三个时间值: 访问时间:最后一次访问文件的时间.例如,cat命令会修改这个时间. 修改时间:文件内容最后一次被修改的时间. 状态更改时间:文件的i节点最后一次被修改的时间.例如 ...