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使用点操作符访问字典(dict)数据的方法
Mar 16 Python
使用pdb模块调试Python程序实例
Jun 02 Python
Python的string模块中的Template类字符串模板用法
Jun 27 Python
一步步教你用Python实现2048小游戏
Jan 19 Python
解决pandas 作图无法显示中文的问题
May 24 Python
python实现简单五子棋游戏
Jun 18 Python
python命令行工具Click快速掌握
Jul 04 Python
Python实现二叉搜索树BST的方法示例
Jul 30 Python
python实现静态服务器
Sep 05 Python
浅谈Python程序的错误:变量未定义
Jun 02 Python
用python画城市轮播地图
May 28 Python
Python+Tkinter打造签名设计工具
Apr 01 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 park、unpark、ord 函数使用方法(二进制流接口应用实例)
2010/10/19 PHP
php 读取文件头判断文件类型的实现代码
2013/08/05 PHP
PHP实现基于栈的后缀表达式求值功能
2017/11/10 PHP
ASP SQL防注入的方法
2008/12/25 Javascript
动态样式类封装JS代码
2009/09/02 Javascript
读jQuery之十 事件模块概述
2011/06/27 Javascript
extjs_02_grid显示本地数据、显示跨域数据
2014/06/23 Javascript
javascript基本类型详解
2014/11/28 Javascript
javascript判断网页是关闭还是刷新
2015/09/12 Javascript
jquery原理以及学习技巧介绍
2015/11/11 Javascript
MVC+jQuery.Ajax异步实现增删改查和分页
2020/12/22 Javascript
详解vue-router2.0动态路由获取参数
2017/06/14 Javascript
VueJs单页应用实现微信网页授权及微信分享功能示例
2017/07/26 Javascript
微信小程序实现横向增长表格的方法
2018/07/24 Javascript
vue3.0 的 Composition API 的使用示例
2020/10/26 Javascript
[00:12]2018DOTA2亚洲邀请赛 sylar表现SOLO技艺
2018/04/06 DOTA
使用Python中PDB模块中的命令来调试Python代码的教程
2015/03/30 Python
python自带的http模块详解
2016/11/06 Python
python中解析json格式文件的方法示例
2017/05/03 Python
Python模块_PyLibTiff读取tif文件的实例
2020/01/13 Python
Python带参数的装饰器运行原理解析
2020/06/09 Python
哪些是python中web开发框架
2020/06/17 Python
详细分析Python垃圾回收机制
2020/07/01 Python
python中return不返回值的问题解析
2020/07/22 Python
html5 touch事件实现触屏页面上下滑动(二)
2016/03/10 HTML / CSS
HTML5实现移动端弹幕动画效果
2019/08/01 HTML / CSS
美国女士泳装店:Swimsuits For All
2017/03/02 全球购物
Weblogc domain问题
2014/01/27 面试题
有关水浒传的读书笔记
2015/06/25 职场文书
学习社交礼仪心得体会
2016/01/22 职场文书
社区志愿者服务心得体会
2016/01/22 职场文书
高中优秀作文(范文)
2019/08/15 职场文书
为什么mysql字段要使用NOT NULL
2021/05/13 MySQL
JavaScript 定时器详情
2021/11/11 Javascript
【海涛DOTA解说】EVE女子战队独家录像加ZSMJ神牛两连发
2022/04/01 DOTA
WinServer2012搭建DNS服务器的方法步骤
2022/06/10 Servers