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实现协同过滤的教程
Apr 08 Python
Python中的random()方法的使用介绍
May 15 Python
深入理解Python变量与常量
Jun 02 Python
Python-嵌套列表list的全面解析
Jun 08 Python
pandas数据处理基础之筛选指定行或者指定列的数据
May 03 Python
基于DataFrame筛选数据与loc的用法详解
May 18 Python
详解Python循环作用域与闭包
Mar 21 Python
Python中dict和set的用法讲解
Mar 28 Python
python实现机器人卡牌
Oct 06 Python
Flask和pyecharts实现动态数据可视化
Feb 26 Python
Python实现获取当前目录下文件名代码详解
Mar 10 Python
python实现简单贪吃蛇游戏
Sep 29 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
杏林同学录(四)
2006/10/09 PHP
PHP三层结构(上) 简单三层结构
2010/07/04 PHP
色色整理的PHP面试题集锦
2012/03/08 PHP
Zend Framework教程之视图组件Zend_View用法详解
2016/03/05 PHP
PHP array_reduce()函数的应用解析
2018/10/28 PHP
PHP面向对象程序设计中的self、static、parent关键字用法分析
2019/08/14 PHP
js获取变量
2006/08/24 Javascript
javascript iframe中打开文件,并检测iframe存在否
2008/12/28 Javascript
jquery ajax 登录验证实现代码
2009/09/23 Javascript
jquery 新建的元素事件绑定问题解决方案
2014/06/12 Javascript
9款2014最热门jQuery实用特效推荐
2014/12/07 Javascript
jQuery中appendTo()方法用法实例
2015/01/08 Javascript
jQuery实现长按按钮触发事件的方法
2015/02/02 Javascript
jQuery内容过滤选择器用法分析
2015/02/10 Javascript
javascript实现网页屏蔽Backspace事件,输入框不屏蔽
2015/07/21 Javascript
js实现网页图片延时加载 提升网页打开速度
2016/01/26 Javascript
jQuery UI仿淘宝搜索下拉列表功能
2017/01/10 Javascript
javascript基础进阶_深入剖析执行环境及作用域链
2017/09/05 Javascript
react 父组件与子组件之间的值传递的方法
2017/09/14 Javascript
详解mpvue开发小程序小总结
2018/07/25 Javascript
JS实现可视化文件上传
2018/09/08 Javascript
通过Nodejs搭建网站简单实现注册登录流程
2019/06/14 NodeJs
vue中实现Monaco Editor自定义提示功能
2019/07/05 Javascript
有趣的JavaScript隐式类型转换操作实例分析
2020/05/02 Javascript
python 公共方法汇总解析
2019/09/16 Python
TensorFlow 显存使用机制详解
2020/02/03 Python
解决python3中os.popen()出错的问题
2020/11/19 Python
CSS3的常见transformation图形变化用法小结
2016/05/13 HTML / CSS
css3实现元素环绕中心点布局的方法示例
2019/01/15 HTML / CSS
StubHub希腊:购买体育赛事、音乐会和剧院门票
2019/08/03 全球购物
会计电算化专业个人的自我评价
2013/11/24 职场文书
房地产项目策划书
2014/02/05 职场文书
公务员爱岗敬业心得体会
2016/01/25 职场文书
《玩出了名堂》教学反思
2016/02/17 职场文书
【海涛教你打DOTA】剑圣第一人称视角解说
2022/04/01 DOTA
一文教你快速生成MySQL数据库关系图
2022/06/28 Redis