Django contrib auth authenticate函数源码解析


Posted in Python onNovember 12, 2020

引言

django提供了一个默认的auth系统用于用户的登录和授权,并提供了一定的扩展性,允许开发者自行定义多个验证后台,每个验证后台必须实现authenticate函数,并返回None或者User对象。

默认的后台是django.contrib.auth.backends.ModelBackend,该后台通过用户名和密码进行用户的验证,以settings.AUTH_USER_MODEL作为模型。但是在实际的开发中,相信大家都不会固定的使用用户名以及同一个model进行验证,比如,不同的角色需要不同的model作为验证的数据源,有的角色是使用手机登录,而有的角色使用邮箱登录。

那么,当存在多个验证后台的时候,django是如何制作一个统一的接口进行不同后台的验证呢?

authenticate函数分析

源码:

def authenticate(**credentials):
  """
  If the given credentials are valid, return a User object.
  """
  for backend, backend_path in _get_backends(return_tuples=True):
    try:
      inspect.getcallargs(backend.authenticate, **credentials)
    except TypeError:
      # This backend doesn't accept these credentials as arguments. Try the next one.
      continue

    try:
      user = backend.authenticate(**credentials)
    except PermissionDenied:
      # This backend says to stop in our tracks - this user should not be allowed in at all.
      break
    if user is None:
      continue
    # Annotate the user object with the path of the backend.
    user.backend = backend_path
    return user

  # The credentials supplied are invalid to all backends, fire signal
  user_login_failed.send(sender=__name__, credentials=_clean_credentials(credentials))

**credentials

首先可以看到authenticate函数接受的参数,这是指authenticate函数只接受关键字传参,位置传参是不允许的。因此在使用authenticate函数的时候注意不要为了省事而位置传参。

# This will fail
user = authenticate('username', 'password')

# This will success
user = authenticate(username='username', password='password')

inspect.getcallargs(func, *args, **kwargs)
inspect模块是Python官方的标准模块,这个模块对Python的自省功能进行一定的封装。其中inspect.getcallargs检查args和kwargs这些参数是否能被func要求的参数匹配,若匹配成功返回参数字典,如果不能匹配就会raise TypeError。
举个简单的例子。假设在Python中定义这样一个函数:

import inspect
def test_func(arg1, arg2, *args, **kwargs):
  pass
# this will raise TypeError
inspect.getcallargs(test_func, a=1, b=2, c=3)
# TypeError: test_func() missing 2 required positional arguments: 'arg1' and 'arg2'

# this will ok
inspect.getcallargs(test_func, 1, 2, 3, a=1, b=2, c=3)
# {'kwargs': {'b': 2, 'c': 3, 'a': 1}, 'arg2': 2, 'args': (3,), 'arg1': 1}

应用场景

通过inspect.getcallargs的参数过滤功能,只要设置不同后台的authenticate的函数参数,就能在第一步实现不同角色的后台选择。

假设有三种角色,角色1使用用户名登录,角色2使用手机登录,角色3使用手机或者邮箱登录,那么如何通过inspect.getcallargs就选择合适的backend.authenticate呢?

def role3_authenticate(role3_phone=None, role3_email=None, password=None):
  print("role1 authentication.")

def role2_authenticate(role2_phone=None, password=None):
  print("role2 authenticate.")

def role1_authenticate(role1_name=None, password=None):
  print("role2 authenticate.")

methods = [role1_authenticate, role2_authenticate, role3_authenticate]
def authenticate(**credentials):
  for backend in methods:
    try:
      inspect.getcallargs(backend, **credentials)
    except TypeError:
      print("error")
      continue

    backend(**credentials)
    print("end")
    break

如果加入**kwargs则每个authenticate都不会引发TypeError,因为其余参数都设置了默认参数,如果确实需要,则之前的参数使用位置传参。

signal

若用户没有成功登陆,则authenticate发送了一个用户没有成功登陆的信号,开发者可以自行定义接受这个信号的recevier。关于django signal笔者之后还会详细谈及。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python中range()与xrange()用法分析
Sep 21 Python
一个基于flask的web应用诞生 flask和mysql相连(4)
Apr 11 Python
Python生成随机密码的方法
Jun 16 Python
python编写分类决策树的代码
Dec 21 Python
python 实现对数据集的归一化的方法(0-1之间)
Jul 17 Python
使用python判断你是青少年还是老年人
Nov 29 Python
python opencv读mp4视频的实例
Dec 07 Python
Django模板语言 Tags使用详解
Sep 09 Python
Python中格式化字符串的四种实现
May 26 Python
Python如何执行精确的浮点数运算
Jul 31 Python
pycharm配置安装autopep8自动规范代码的实现
Mar 02 Python
Python中递归以及递归遍历目录详解
Oct 24 Python
python 获取字典键值对的实现
Nov 12 #Python
Sentry错误日志监控使用方法解析
Nov 12 #Python
python 利用opencv实现图像网络传输
Nov 12 #Python
Anaconda详细安装步骤图文教程
Nov 12 #Python
Jupyter Notebook安装及使用方法解析
Nov 12 #Python
利用Python发送邮件或发带附件的邮件
Nov 12 #Python
Python如何使用ConfigParser读取配置文件
Nov 12 #Python
You might like
基于mysql的论坛(4)
2006/10/09 PHP
php导出word文档与excel电子表格的简单示例代码
2014/03/08 PHP
PHP多维数组排序array详解
2017/11/21 PHP
jQuery学习2 选择器的使用说明
2010/02/07 Javascript
Ubuntu 11.10 安装Node.js的方法
2011/11/30 Javascript
利用js的Node遍历找到repeater的一个字段实例介绍
2013/04/25 Javascript
使用JavaScript 实现对象 匀速/变速运动的方法
2013/05/08 Javascript
JQuery中SetTimeOut传参问题探讨
2013/05/10 Javascript
jQuery实现倒计时按钮功能代码分享
2014/09/03 Javascript
网页收藏夹显示ICO图标(代码少)
2015/08/04 Javascript
详解JavaScript操作HTML DOM的基本方式
2015/10/21 Javascript
AngularJs动态加载模块和依赖注入详解
2016/01/11 Javascript
jQuery实现加入收藏夹功能(主流浏览器兼职)
2016/12/24 Javascript
bootstrap手风琴制作方法详解
2017/01/11 Javascript
vue如何集成raphael.js中国地图的方法示例
2017/08/15 Javascript
使用Angular CLI生成 Angular 5项目教程详解
2018/03/18 Javascript
JS实现随机生成10个手机号的方法示例
2018/12/07 Javascript
运用js实现图层拖拽的功能
2019/05/24 Javascript
kafka调试中遇到Connection to node -1 could not be established. Broker may not be available.
2019/09/17 Javascript
js实现图片实时时钟
2020/01/15 Javascript
vue elementui 实现搜索栏公共组件封装的实例代码
2020/01/20 Javascript
Python random模块常用方法
2014/11/03 Python
django批量导入xml数据
2016/10/16 Python
Flask之flask-script模块使用
2018/07/26 Python
Python判断两个文件是否相同与两个文本进行相同项筛选的方法
2019/03/01 Python
Python基本语法之运算符功能与用法详解
2019/10/22 Python
What's the difference between an interface and abstract class? (接口与抽象类有什么区别)
2012/10/29 面试题
Linux如何压缩可执行文件
2013/10/21 面试题
师范生自荐信
2013/10/27 职场文书
《开国大典》教学反思
2014/04/19 职场文书
节电标语大全
2014/06/23 职场文书
暑期学习心得体会
2014/09/02 职场文书
依法行政工作汇报材料
2014/10/28 职场文书
元旦主持词开场白
2015/05/29 职场文书
军训新闻稿范文
2015/07/17 职场文书
一篇文章看懂MySQL主从复制与读写分离
2021/11/07 MySQL