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时间整形转标准格式的示例分享
Feb 14 Python
使用Python编写提取日志中的中文的脚本的方法
Apr 30 Python
Python的Flask框架及Nginx实现静态文件访问限制功能
Jun 27 Python
python开发利器之ulipad的使用实践
Mar 16 Python
详细分析python3的reduce函数
Dec 05 Python
python RabbitMQ 使用详细介绍(小结)
Nov 08 Python
python将一组数分成每3个一组的实例
Nov 14 Python
python opencv 图像拼接的实现方法
Jun 27 Python
python实现美团订单推送到测试环境,提供便利操作示例
Aug 09 Python
Python collections模块使用方法详解
Aug 28 Python
python实现指定ip端口扫描方式
Dec 17 Python
详解pandas映射与数据转换
Jan 22 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
php使用GeoIP库实例
2014/06/27 PHP
php购物车实现方法
2015/01/03 PHP
浅析PHP中call user func()函数及如何使用call user func调用自定义函数
2015/11/05 PHP
PHP分页初探 一个最简单的PHP分页代码的简单实现
2016/06/21 PHP
基于PHP实现邮箱验证激活过程详解
2020/10/28 PHP
JavaScript写的一个DIV 弹出网页对话框
2009/08/14 Javascript
iframe 父窗口和子窗口相互的调用方法集锦
2010/12/15 Javascript
完美JQuery图片切换效果的简单实现
2016/07/21 Javascript
jquery validate表单验证插件
2016/09/06 Javascript
js编写的treeview使用方法
2016/11/11 Javascript
Node.js对MongoDB数据库实现模糊查询的方法
2017/05/03 Javascript
vue 2.0封装model组件的方法
2017/08/03 Javascript
mongoose设置unique不生效问题的解决及如何移除unique的限制
2017/11/07 Javascript
解决linux下node.js全局模块找不到的问题
2018/05/15 Javascript
转换layUI的数据表格中的日期格式方法
2019/09/19 Javascript
JS防抖和节流实例解析
2019/09/24 Javascript
在vue中实现嵌套页面(iframe)
2020/07/30 Javascript
[03:08]迎霜节狂欢!2018年迎霜节珍藏Ⅰ一览
2018/12/25 DOTA
Python动态语言与鸭子类型详解
2019/07/01 Python
python获取栅格点和面值的实现
2020/03/10 Python
Python生成器实现简单"生产者消费者"模型代码实例
2020/03/27 Python
python代码实现将列表中重复元素之间的内容全部滤除
2020/05/22 Python
Python如何使用ConfigParser读取配置文件
2020/11/12 Python
美国LOGO设计公司:The Logo Company
2018/07/16 全球购物
行政经理的岗位职责
2013/11/23 职场文书
毕业生个人的求职信范文
2013/12/03 职场文书
运动会广播稿20字
2014/02/18 职场文书
六个一活动实施方案
2014/03/21 职场文书
媒矿安全生产承诺书
2014/05/23 职场文书
大学学雷锋活动总结
2014/06/26 职场文书
小城镇建设汇报材料
2014/08/16 职场文书
庆国庆活动总结
2014/08/28 职场文书
在校学生证明格式
2015/06/24 职场文书
四十年同学聚会致辞
2015/07/28 职场文书
送给教师们,到底该如何写好教学反思?
2019/07/02 职场文书
Win11局域网共享权限在哪里设置? Win11高级共享的设置技巧
2022/04/05 数码科技