指定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
相关产品推荐
相关产品推荐

