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求pi的方法
Oct 08 Python
python根据给定文件返回文件名和扩展名的方法
Mar 27 Python
Python实现简易版的Web服务器(推荐)
Jan 29 Python
Python实现线程状态监测简单示例
Mar 28 Python
python print 按逗号或空格分隔的方法
May 02 Python
Python 创建空的list,以及append用法讲解
May 04 Python
Python 等分切分数据及规则命名的实例代码
Aug 16 Python
Python银行系统实战源码
Oct 25 Python
Django如何使用redis作为缓存
May 21 Python
MATLAB数学建模之画图汇总
Jul 16 Python
Python django框架 web端视频加密的实例详解
Nov 20 Python
利用python+ffmpeg合并B站视频及格式转换的实例代码
Nov 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
PHP中static关键字原理的学习研究分析
2011/07/18 PHP
PHP递归算法的详细示例分析
2013/02/19 PHP
PHP实现文字写入图片功能
2019/02/18 PHP
可输入的下拉框
2006/06/19 Javascript
有一段有意思的代码-javascript现实多行信息
2007/08/26 Javascript
动态载入/删除/更新外部 JavaScript/Css 文件的代码
2010/07/03 Javascript
javascript提取URL的搜索字符串中的参数(自定义函数实现)
2013/01/22 Javascript
使用GruntJS构建Web程序之构建篇
2014/06/04 Javascript
jQuery验证元素是否为空的两种常用方法
2015/03/17 Javascript
js兼容火狐显示上传图片预览效果的方法
2015/05/21 Javascript
使用AngularJS创建单页应用的编程指引
2015/06/19 Javascript
详解JavaScript对Date对象的操作问题(生成一个倒数7天的数组)
2015/10/01 Javascript
安装使用Mongoose配合Node.js操作MongoDB的基础教程
2016/03/01 Javascript
微信小程序 数据绑定详解及实例
2016/10/25 Javascript
jQuery动态移除和添加背景图片的方法详解
2017/03/07 Javascript
Vue.js实战之利用vue-router实现跳转页面
2017/04/01 Javascript
微信小程序实现跟随菜单效果和循环嵌套加载数据
2017/11/21 Javascript
使用node.js实现微信小程序实时聊天功能
2018/08/13 Javascript
Element UI框架中巧用树选择器的实现
2018/12/12 Javascript
微信小程序云开发如何使用npm安装依赖
2019/05/18 Javascript
微信小程序静默登录的实现代码
2020/01/08 Javascript
python中list常用操作实例详解
2015/06/03 Python
Python搭建HTTP服务器和FTP服务器
2017/03/09 Python
numpy:找到指定元素的索引示例
2019/11/26 Python
python excel和yaml文件的读取封装
2021/01/12 Python
使用spring mvc+localResizeIMG实现HTML5端图片压缩上传的功能
2016/12/16 HTML / CSS
加州风格的游泳和沙滩装品牌:Cupshe
2019/06/10 全球购物
斯洛伐克香水和化妆品购物网站:Parfemy-Elnino.sk
2020/01/28 全球购物
介绍一下write命令
2012/09/24 面试题
后备干部考察材料
2014/02/12 职场文书
工作经验交流材料
2014/12/30 职场文书
2015年创先争优活动总结
2015/03/27 职场文书
2015年禁毒工作总结
2015/04/30 职场文书
2015年学校综合治理工作总结
2015/07/20 职场文书
贴吧吧主申请感言
2015/08/03 职场文书
简单总结SpringMVC拦截器的使用方法
2021/06/28 Java/Android