You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

指定Django认证后端被忽略问题求助

问题

自定义了两个Django认证后端(标准登录、双因素登录),已在settings.py中配置:

AUTHENTICATION_BACKENDS = [
    'user_profile.auth_backends.TOTPBackend',
    'user_profile.auth_backends.StandardLoginBackend',
    'django.contrib.auth.backends.ModelBackend',
]

后端文件user_profile/auth_backends.py中的类实现如下:

class TOTPBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, twofa_token=None, **kwargs):
        print("In TOTPBackend")
        # 尝试用默认方法认证
        user = super().authenticate(request, username=username, password=password, **kwargs)
        
        if user is not None:
            # 省略后续逻辑
            ...
class StandardLoginBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        print("In StandardLoginBackend")

        # 尝试用默认方法认证
        user = super().authenticate(request, username=username, password=password, **kwargs)
        
        if user is not None:
            # 省略后续逻辑
            ...

在视图中调用authenticate时指定了backend参数,期望调用对应后端:

class TwoFaLogin(APIView):
    permission_classes = (AllowAny,)

    def post(self, request):
        print("TwoFaLogin View")
        username = request.data['username']
        passwd = request.data['password']
        twofa_token = request.data['twofa_token']

        user = authenticate(request, username=username, password=passwd, twofa_token=twofa_token, backend='user_profile.auth_backends.TOTPBackend')
class StandardLogin(APIView):
    permission_classes = (AllowAny,)

    def post(self, request):
        print("StandardLogin View")
        username = request.data['username']
        passwd = request.data['password']
        user = authenticate(request, username=username, password=passwd, backend='user_profile.auth_backends.StandardLoginBackend')

但实际backend参数被忽略,认证始终按AUTHENTICATION_BACKENDS的配置顺序执行,调整顺序会改变调用顺序,但指定参数无效。

原因分析

核心问题出在自定义后端中调用的super().authenticate():ModelBackend的authenticate方法内部会触发整个认证后端链的遍历逻辑,相当于绕开了你指定单一后端的限制。即使你在authenticate函数中指定了某个后端,父类方法的调用会重新遍历所有配置的后端,导致所有后端被依次执行。

解决方案

1. 移除父类authenticate调用,自行实现认证逻辑

直接在自定义后端中完成用户查询、密码验证和专属逻辑,不依赖父类方法触发全局后端链:

from django.contrib.auth import get_user_model
from django.contrib.auth.hashers import check_password

class TOTPBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, twofa_token=None, **kwargs):
        print("In TOTPBackend")
        User = get_user_model()
        try:
            user = User.objects.get(username=username)
            # 验证密码
            if not check_password(password, user.password):
                return None
            # 验证TOTP令牌
            if self._verify_totp_token(user, twofa_token):
                return user
        except User.DoesNotExist:
            return None
        return None

    def _verify_totp_token(self, user, token):
        # 这里实现你的TOTP验证逻辑
        ...
class StandardLoginBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        print("In StandardLoginBackend")
        User = get_user_model()
        try:
            user = User.objects.get(username=username)
            if check_password(password, user.password):
                # 标准登录的额外逻辑(如果需要)
                return user
        except User.DoesNotExist:
            return None
        return None

2. 直接实例化目标后端,绕过全局authenticate函数

在视图中直接创建指定后端的实例,调用其authenticate方法,完全避免后端链遍历:

from user_profile.auth_backends import TOTPBackend, StandardLoginBackend
from django.contrib.auth import login

class TwoFaLogin(APIView):
    permission_classes = (AllowAny,)

    def post(self, request):
        print("TwoFaLogin View")
        username = request.data['username']
        passwd = request.data['password']
        twofa_token = request.data['twofa_token']

        # 直接实例化指定后端
        backend = TOTPBackend()
        user = backend.authenticate(request, username=username, password=passwd, twofa_token=twofa_token)
        
        if user:
            login(request, user)
            # 返回登录成功响应
            ...
class StandardLogin(APIView):
    permission_classes = (AllowAny,)

    def post(self, request):
        print("StandardLogin View")
        username = request.data['username']
        passwd = request.data['password']

        backend = StandardLoginBackend()
        user = backend.authenticate(request, username=username, password=passwd)
        
        if user:
            login(request, user)
            # 返回登录成功响应
            ...

3. 二次确认backend参数路径

确保指定的后端路径和settings.py中的配置完全一致,注意Python模块名的大小写敏感性,避免拼写错误。

内容的提问来源于stack exchange,提问作者Bring Coffee Bring Beer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:22:46