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 相关文章推荐
详解Django通用视图中的函数包装
Jul 21 Python
Python cx_freeze打包工具处理问题思路及解决办法
Feb 13 Python
Python常用库推荐
Dec 04 Python
Python爬虫之xlml解析库(全面了解)
Aug 08 Python
Python实现的端口扫描功能示例
Apr 08 Python
使用Django2快速开发Web项目的详细步骤
Jan 06 Python
python 接口实现 供第三方调用的例子
Aug 13 Python
Python Subprocess模块原理及实例
Aug 26 Python
Python 继承,重写,super()调用父类方法操作示例
Sep 29 Python
如何使用pandas读取txt文件中指定的列(有无标题)
Mar 05 Python
python Zmail模块简介与使用示例
Dec 19 Python
pycharm远程连接服务器并配置python interpreter的方法
Dec 23 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
后宫无数却洁身自好的男主,唐三只爱小舞
2020/03/02 国漫
浅谈php serialize()与unserialize()的用法
2013/06/05 PHP
laravel5.6实现数值转换
2019/10/23 PHP
JS高级笔记
2011/07/13 Javascript
jquery插件uploadify实现带进度条的文件批量上传
2015/12/13 Javascript
AngularJS 依赖注入详解和简单实例
2016/07/28 Javascript
JS实现线性表的链式表示方法示例【经典数据结构】
2017/04/11 Javascript
详解Angular-Cli中引用第三方库
2017/05/21 Javascript
vue视图不更新情况详解
2019/05/16 Javascript
vue.js实现左边导航切换右边内容
2019/10/21 Javascript
小程序实现上传视频功能
2020/08/18 Javascript
Vue 组件注册全解析
2020/12/17 Vue.js
[06:04]DOTA2国际邀请赛纪录片:Just For LGD
2013/08/11 DOTA
[00:27]DOTA2次级职业联赛 - Lilith战队宣传片
2014/12/01 DOTA
Python实现端口复用实例代码
2014/07/03 Python
Python中使用hashlib模块处理算法的教程
2015/04/28 Python
Django中的Signal代码详解
2018/02/05 Python
python字符串string的内置方法实例详解
2018/05/14 Python
python cumsum函数的具体使用
2019/07/29 Python
Python 中判断列表是否为空的方法
2019/11/24 Python
推荐8款常用的Python GUI图形界面开发框架
2020/02/23 Python
如何设置PyCharm中的Python代码模版(推荐)
2020/11/20 Python
python 视频下载神器(you-get)的具体使用
2021/01/06 Python
详解Python爬虫爬取博客园问题列表所有的问题
2021/01/18 Python
html5调用摄像头功能的实现代码
2018/05/07 HTML / CSS
日本最新流行服饰网购:Nissen
2016/07/24 全球购物
ECCO英国官网:丹麦鞋履品牌
2019/09/03 全球购物
Ticketmaster意大利:音乐会、节日、艺术和剧院的官方门票
2019/12/23 全球购物
伦敦鲜花递送:Flower Station
2021/02/03 全球购物
会计电算化应届生求职信
2013/11/03 职场文书
关于环保的建议书
2014/05/12 职场文书
档案信息化建设方案
2014/05/16 职场文书
关于青春的演讲稿500字
2014/08/22 职场文书
2014年大班保育员工作总结
2014/12/02 职场文书
污染环境建议书
2015/09/14 职场文书
未来,这5大方向都很适合创业
2019/07/22 职场文书