Django自定义用户模型登录异常:正确邮箱密码无法登录
嘿,我看了你的代码,发现几个关键问题,正是这些导致你注册成功但登录失败的,咱们一个个来修复:
1. 密码被重复哈希(signup函数的致命错误)
你的注册函数里,在form.save(commit=True)之后又调用了user.set_password(user.password),这会把已经哈希过的密码再哈希一次!
UserCreationForm的save()方法已经自动帮你把用户输入的明文密码哈希后存储了,此时user.password是已经哈希好的字符串,你再调用set_password会把这个哈希字符串当成明文重新哈希,最终导致存储的密码和用户输入的完全不匹配。
修复方法:删掉这两行冗余代码
# signup函数里删掉以下两行 user.set_password(user.password) user.save()
2. 自定义用户模型缺少必要的UserManager
继承AbstractBaseUser必须配套自定义的UserManager,否则Django的用户认证系统会出现各种隐性问题。你现在的CustomUser没有设置Manager,这会导致很多内置功能失效。
在models.py里添加CustomUserManager并关联到模型
from django.db import models from django.contrib.auth.models import AbstractBaseUser, BaseUserManager # 先写自定义的Manager class CustomUserManager(BaseUserManager): def create_user(self, email, first_name, last_name, password=None): if not email: raise ValueError('用户必须填写邮箱') # 标准化邮箱(转小写) email = self.normalize_email(email) user = self.model(email=email, first_name=first_name, last_name=last_name) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, first_name, last_name, password): user = self.create_user(email, first_name, last_name, password) user.is_staff = True user.save(using=self._db) return user # 然后修改你的CustomUser模型 class CustomUser(AbstractBaseUser): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) email = models.EmailField(unique=True, max_length=255) is_staff = models.BooleanField(default=False) is_active = models.BooleanField(default=True) REQUIRED_FIELDS = ['first_name', 'last_name'] USERNAME_FIELD = 'email' # 关联自定义Manager objects = CustomUserManager() # 删掉你写的get__by_natural_key方法,多了一个下划线,而且AbstractBaseUser已经实现了这个方法
3. EmailAuthenticationForm的clean方法未完成
你的clean_username方法在捕获CustomUser.DoesNotExist异常后没有任何处理,既没有返回值也没有抛出验证错误,会导致表单验证逻辑异常。
修复forms.py里的EmailAuthenticationForm
class EmailAuthenticationForm(AuthenticationForm): username = forms.EmailField(widget=forms.EmailInput(attrs={'autofocus': True})) def clean_username(self): username = self.cleaned_data.get('username') try: CustomUser.objects.get(email=username) return username except CustomUser.DoesNotExist: # 抛出明确的验证错误 raise forms.ValidationError("该邮箱尚未注册")
4. 配置认证后端和自定义用户模型
你写了EmailBackend,但需要告诉Django使用它,同时必须指定自定义用户模型:
在settings.py里添加以下配置
# 指定自定义用户模型 AUTH_USER_MODEL = '你的app名称.CustomUser' # 配置认证后端 AUTHENTICATION_BACKENDS = [ '你的app名称.backends.EmailBackend', # 可选:保留默认后端,方便管理后台登录 'django.contrib.auth.backends.ModelBackend', ]
5. 登录函数的优化(可选但更规范)
不需要手动实例化EmailBackend,Django的authenticate函数会自动使用你配置的后端,这样代码更符合最佳实践:
def index(request): # 先判断用户是否已登录,避免重复登录 if request.user.is_authenticated: return redirect('home') if request.method == 'POST': form = EmailAuthenticationForm(data=request.POST) if form.is_valid(): email = form.cleaned_data.get('username') password = form.cleaned_data.get('password') # 用Django自带的authenticate,自动匹配配置的后端 user = authenticate(request, username=email, password=password) if user is not None: login(request, user) return redirect('home') else: # 给表单添加全局错误提示 form.add_error(None, "邮箱或密码错误") else: form = EmailAuthenticationForm() return render(request, 'index.html', {'form': form})
最后一步:重置数据库(开发环境)
如果你之前已经运行过迁移并创建了用户,建议清空当前数据库(比如删除MySQL里的对应库,或者开发用的sqlite文件),然后重新执行:
python manage.py makemigrations python manage.py migrate
做完这些修改后,你的登录系统应该就能正常工作了!
内容的提问来源于stack exchange,提问作者0973

