Django自定义AbstractBaseUser模型authenticate登录验证失败如何解决?
问题根因&解决步骤
1. 模型字段定义错误
你自定义的User模型重写了AbstractBaseUser已经自带的password字段,并且把max_length设为25:
# 这行代码是错误的,直接删掉 password = models.CharField(max_length = 25)
Django的密码加密后长度远大于25位,直接截断会导致密码哈希值损坏,校验永远失败。AbstractBaseUser已经内置了符合要求的password字段,不需要自己定义。
同时你的is_active字段默认值为False,Django默认的authenticate方法只会返回is_active=True的用户,如果你没有手动修改用户激活状态,校验也会失败。如果你的业务逻辑允许用户注册后直接登录,可以把默认值改为True:
is_active = models.BooleanField(default = True)
2. 密码存储方式错误
你自己遍历对比明文密码能成功,说明你创建用户时直接存储了明文密码,没有做加密处理。authenticate方法会自动把传入的明文密码加密后,和数据库存储的哈希值对比,两者当然匹配不上。
创建用户时必须使用set_password()方法处理明文密码,或者调用自定义管理器的create_user方法,禁止直接给password字段赋值明文:
# 错误写法,禁止使用 user = User(username="xxx", password="123456") user.save() # 正确写法1:手动调用set_password user = User(username="xxx", email="xxx@xx.com", phone="13xxxxxxxxx", first_name="xx", last_name="xx") user.set_password("你的明文密码") # 这一步会自动加密密码 user.save() # 正确写法2:调用create_user方法(需要你的管理器正确实现) user = User.objects.create_user( username="xxx", email="xxx@xx.com", phone="13xxxxxxxxx", first_name="xx", last_name="xx", password="你的明文密码" )
3. 检查自定义管理器实现
你的UserProfileManager必须继承BaseUserManager,并且正确实现create_user、create_superuser方法,内部必须调用set_password处理密码,示例如下:
from django.contrib.auth.base_user import BaseUserManager class UserProfileManager(BaseUserManager): def create_user(self, username, email, phone, first_name, last_name, password=None): if not email: raise ValueError('用户必须提供邮箱') email = self.normalize_email(email) user = self.model( username=username, email=email, phone=phone, first_name=first_name, last_name=last_name ) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, username, email, phone, first_name, last_name, password): user = self.create_user( username=username, email=email, phone=phone, first_name=first_name, last_name=last_name, password=password ) user.is_staff = True user.is_superuser = True user.is_active = True user.save(using=self._db) return user
4. 生效修改
所有代码修改完成后,执行迁移命令更新数据库:
python manage.py makemigrations python manage.py migrate
注意:之前已经存入数据库的用户密码都是无效的,需要重新创建用户或者调用set_password重新设置密码才能正常登录。
修改完成后你原本的authenticate逻辑就可以正常运行,登录后会自动写入用户会话,@login_required装饰器也会正常生效。
内容的提问来源于stack exchange,提问作者Jyothis Benny

