使用自定义认证后端及模型时Django无法正常登录的问题排查
Django自定义认证后端返回有效User实例但登录后仍被重定向回登录页
问题背景
对接已有数据库,原有用户表结构与Django默认User差异极大,通过inspectdb生成非托管模型(managed=False),无法继承AbstractUser,因此采用自定义认证后端+Profile关联模型方案:
- 自定义后端验证原有
CustomUser的用户名密码 - 验证通过后,通过关联模型
Profile获取或创建Django原生User实例并返回
异常现象
- 自定义
authenticate()方法可正常返回有效User实例,无效凭证会正确提示"Invalid Credentials" - 登录后始终被重定向回登录页,无法访问受保护资源;使用原生
User登录则完全正常 - 检查
request.session和request.user均已被设置,问题疑似出在django.contrib.auth.login环节
相关代码
settings.py中的认证后端配置
AUTHENTICATION_BACKENDS = [ 'Authentication.custom.CustomAuth', 'django.contrib.auth.backends.ModelBackend' ]
自定义认证后端的authenticate()方法
def authenticate(self, request, username=None, password=None): try: c_user = CustomUser.objects.get(username=username) except CustomUser.DoesNotExist: return None #pwd_valid = check_password(password, user.password) if not c_user.password==password: return None #Get and return the django user object try: profile = Profile.objects.get(custom_user=c_user) user = profile.user #Create user if profile has none if not user: user = User.objects.create( username=''.join(secrets.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(24)) ) profile.user = user profile.save() #Create new profile if none exists except Profile.DoesNotExist: #Create new user for the profile user = User.objects.create( username=''.join(secrets.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(24)) ) Profile.objects.create( user = user, custom_user = c_user ) return user
可能的原因及修复方案
1. 未实现get_user()方法(核心问题)
继承BaseBackend时,必须同时覆写authenticate()和get_user()方法。Django登录后会通过get_user()根据用户ID重新获取实例,若未实现该方法,会导致后续认证验证失败,从而被重定向回登录页。
修复代码:在CustomAuth类中添加以下方法:
from django.contrib.auth.models import User def get_user(self, user_id): try: return User.objects.get(pk=user_id) except User.DoesNotExist: return None
2. 密码验证逻辑不安全且可能失效
当前直接对比明文密码c_user.password==password,若原有数据库的密码是哈希存储的,该判断会直接失败,同时明文对比存在极大安全风险。
修复代码:使用Django内置的密码哈希验证方法:
from django.contrib.auth.hashers import check_password # 替换原有的密码判断逻辑 if not check_password(password, c_user.password): return None
3. Profile关联逻辑优化(可选)
若Profile与User是OneToOneField关联,profile.user不会返回None,只会抛出DoesNotExist异常,因此if not user:的判断可以移除,避免无效逻辑。
4. 会话保存确认(排查用)
虽然Django默认会自动保存会话,但可在登录视图中手动触发保存,确保会话数据持久化:
from django.contrib.auth import login def login_view(request): # 认证逻辑... login(request, user) request.session.save() # 手动触发会话保存 # 重定向逻辑...
内容的提问来源于stack exchange,提问作者George
相关产品推荐
相关产品推荐

