问题描述
我正在尝试实现一种身份验证方法,其中:
- 用户提交他们的电子邮件
- 我为存储在数据库中的该用户生成一个令牌,或者如果该令牌已经存在,我将检索该令牌
- 我生成一个链接以登录并将其通过电子邮件发送给用户,并将令牌作为HTTP参数
- 令牌是从链接中提取的,用于搜索活跃用户
- 用户信息将传递到模板
请注意,这不适用于任何关键任务生产软件-我正在阅读《服从测试山羊O'Reilly》一书,这是作者为我们实施的身份验证方法。
因此,当用户单击电子邮件中的链接时,这是处理该链接的视图功能:
from django.contrib.auth import authenticate,login
def login(request):
uid = request.GET.get('uid')
user = authenticate(uid=uid)
if user is not None:
login(request,user)
return redirect('/')
在该视图函数中,我们调用由authenticate
提供的django.contrib.auth
函数。这是该功能:
def authenticate(request=None,**credentials):
"""
If the given credentials are valid,return a User object.
"""
for backend,backend_path in _get_backends(return_tuples=True):
backend_signature = inspect.signature(backend.authenticate)
try:
backend_signature.bind(request,**credentials)
except TypeError:
# This backend doesn't accept these credentials as arguments. Try the next one.
### I have confirmed that the bind function above,and specifically the _bind private function,### is throwing the TypeError so my custom auth function is never getting called
continue
try:
user = backend.authenticate(request,**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
请注意,这是Django框架代码-我没有编写此代码。由于某些原因,行backend_signature.bind(request,**credentials)
抛出了TypeError
,因此未调用我的自定义auth函数。 (我已经通过打印语句确认了这一点)
这里是AUTHENTICATION_BACKENDS
中的settings.py
。 _get_backends
语句中的for
函数将查找此设置,以确定应使用哪种身份验证后端。我可以确认它找到了我的自定义后端-由于某种原因,传入的参数无法绑定到它:
AUTHENTICATION_BACKENDS = [
'accounts.authentication.PasswordlessAuthenticationBackend',]
def authenticate(self,uid):
if not Token.objects.filter(uid=uid).exists():
return None
token = Token.objects.get(uid=uid)
try:
user = ListUser.objects.get(email=token.email)
return user
except ListUser.DoesNotExist:
return ListUser.objects.create(email=token.email)
在此先感谢您的帮助-如果您可以提供更多信息,请告诉我。
解决方法
想通了。
我的身份验证功能需要带一个请求参数,因为默认情况下为None
的请求参数是从Django authenticate
函数中层叠下来的。
所以我的自定义身份验证功能签名看起来像这样:
authenticate(self,request,uid)