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正则表达式匹配ip地址实例
Oct 09 Python
Python图像灰度变换及图像数组操作
Jan 27 Python
Python列表和元组的定义与使用操作示例
Jul 26 Python
Python实现文件内容批量追加的方法示例
Aug 29 Python
python正则表达式去除两个特殊字符间的内容方法
Dec 24 Python
Python中文编码知识点
Feb 18 Python
python 搜索大文件的实例代码
Jul 08 Python
Django发送邮件和itsdangerous模块的配合使用解析
Aug 10 Python
Python3 Tkinkter + SQLite实现登录和注册界面
Nov 19 Python
如何在 Matplotlib 中更改绘图背景的实现
Nov 26 Python
pycharm如何设置官方中文(如何汉化)
Dec 29 Python
python中封包建立过程实例
Feb 18 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
建立动态的WML站点(三)
2006/10/09 PHP
php Memcache 中实现消息队列
2009/11/24 PHP
PHP实现的封装验证码类详解
2013/06/18 PHP
php使用fsockopen函数发送post,get请求获取网页内容的方法
2014/11/15 PHP
WordPress中用于获取文章信息以及分类链接的函数用法
2015/12/18 PHP
PHP简单实现数字分页功能示例
2016/08/24 PHP
PHP大文件分割上传 PHP分片上传
2017/08/28 PHP
用JavaScript玩转游戏物理(一)运动学模拟与粒子系统
2010/06/19 Javascript
DIY jquery plugin - tabs标签切换实现代码
2010/12/11 Javascript
JQuery实现当鼠标停留在某区域3秒后自动执行
2014/09/09 Javascript
js window对象属性和方法相关资料整理
2015/11/11 Javascript
浅谈JavaScript的全局变量与局部变量
2016/06/10 Javascript
JavaScript中获取HTML元素值的三种方法
2016/06/20 Javascript
解析JavaScript数组方法reduce
2016/12/12 Javascript
深究AngularJS中ng-drag、ng-drop的用法
2017/06/12 Javascript
Angular之toDoList的实现代码示例
2017/12/02 Javascript
基于jquery实现左右上下移动效果
2018/05/02 jQuery
[43:33]EG vs Spirit Supermajor 败者组 BO3 第一场 6.4
2018/06/05 DOTA
python数据结构之二叉树的建立实例
2014/04/29 Python
python基本语法练习实例
2017/09/19 Python
python基础教程项目二之画幅好画
2018/04/02 Python
python 异或加密字符串的实例
2018/10/14 Python
DES加密解密算法之python实现版(图文并茂)
2018/12/06 Python
pytorch 求网络模型参数实例
2019/12/30 Python
Python处理PDF与CDF实例
2020/02/26 Python
python如何获得list或numpy数组中最大元素对应的索引
2020/11/16 Python
世界上最大的皮肤科医生拥有和经营的美容网站:LovelySkin
2021/01/03 全球购物
英国顶尖手表珠宝品牌独家授权经销商:HS Johnson
2020/10/28 全球购物
创业计划书中要认真思考的问题
2013/12/28 职场文书
大学生职业生涯规划书模板
2014/01/03 职场文书
外贸业务员工作职责
2014/01/06 职场文书
创建绿色学校先进个人材料
2014/08/20 职场文书
教师个人自我评价
2015/03/04 职场文书
健康教育主题班会
2015/08/14 职场文书
Spring Data JPA的Audit功能审计数据库的变更
2021/06/26 Java/Android
Golang并发操作中常见的读写锁详析
2021/08/30 Golang