Django contrib auth authenticate函数源码解析
引言
django提供了一个默认的auth系统用于用户的登录和授权,并提供了一定的扩展性,允许开发者自行定义多个验证后台,每个验证后台必须实现authenticate函数,并返回None或者User对象。
默认的后台是django.contrib.auth.backends.ModelBackend,该后台通过用户名和密码进行用户的验证,以settings.AUTH_USER_MODEL作为模型。但是在实际的开发中,相信大家都不会固定的使用用户名以及同一个model进行验证,比如,不同的角色需要不同的model作为验证的数据源,有的角色是使用手机登录,而有的角色使用邮箱登录。
那么,当存在多个验证后台的时候,django是如何制作一个统一的接口进行不同后台的验证呢?
authenticate函数分析
源码:
defauthenticate(**credentials): """ Ifthegivencredentialsarevalid,returnaUserobject. """ forbackend,backend_pathin_get_backends(return_tuples=True): try: inspect.getcallargs(backend.authenticate,**credentials) exceptTypeError: #Thisbackenddoesn'tacceptthesecredentialsasarguments.Trythenextone. continue try: user=backend.authenticate(**credentials) exceptPermissionDenied: #Thisbackendsaystostopinourtracks-thisusershouldnotbeallowedinatall. break ifuserisNone: continue #Annotatetheuserobjectwiththepathofthebackend. user.backend=backend_path returnuser #Thecredentialssuppliedareinvalidtoallbackends,firesignal user_login_failed.send(sender=__name__,credentials=_clean_credentials(credentials))
**credentials
首先可以看到authenticate函数接受的参数,这是指authenticate函数只接受关键字传参,位置传参是不允许的。因此在使用authenticate函数的时候注意不要为了省事而位置传参。
#Thiswillfail
user=authenticate('username','password')#Thiswillsuccess
user=authenticate(username='username',password='password')
inspect.getcallargs(func,*args,**kwargs)
inspect模块是Python官方的标准模块,这个模块对Python的自省功能进行一定的封装。其中inspect.getcallargs检查args和kwargs这些参数是否能被func要求的参数匹配,若匹配成功返回参数字典,如果不能匹配就会raiseTypeError。
举个简单的例子。假设在Python中定义这样一个函数:
importinspect deftest_func(arg1,arg2,*args,**kwargs): pass #thiswillraiseTypeError inspect.getcallargs(test_func,a=1,b=2,c=3) #TypeError:test_func()missing2requiredpositionalarguments:'arg1'and'arg2' #thiswillok 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呢?
defrole3_authenticate(role3_phone=None,role3_email=None,password=None): print("role1authentication.") defrole2_authenticate(role2_phone=None,password=None): print("role2authenticate.") defrole1_authenticate(role1_name=None,password=None): print("role2authenticate.") methods=[role1_authenticate,role2_authenticate,role3_authenticate] defauthenticate(**credentials): forbackendinmethods: try: inspect.getcallargs(backend,**credentials) exceptTypeError: print("error") continue backend(**credentials) print("end") break
如果加入**kwargs则每个authenticate都不会引发TypeError,因为其余参数都设置了默认参数,如果确实需要,则之前的参数使用位置传参。
signal
若用户没有成功登陆,则authenticate发送了一个用户没有成功登陆的信号,开发者可以自行定义接受这个信号的recevier。关于djangosignal笔者之后还会详细谈及。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。