Django身份验证功能尝试绑定到自定义身份验证功能时发生TypeError

时间:2020-09-14 18:57:35

标签: python django authentication django-authentication

我正在尝试实现一种身份验证方法,其中:

  • 用户提交他们的电子邮件
  • 我为存储在数据库中的该用户生成一个令牌,或者如果该令牌已经存在,我将检索该令牌
  • 我生成一个链接以登录并将其通过电子邮件发送给用户,并将令牌作为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)

在此先感谢您的帮助-如果您可以提供更多信息,请告诉我。

1 个答案:

答案 0 :(得分:0)

想通了。

我的身份验证功能需要带一个请求参数,因为默认情况下为None的请求参数是从Django authenticate函数中层叠下来的。

所以我的自定义身份验证功能签名看起来像这样:

authenticate(self, request, uid)