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

Django自定义用户模型登录异常:正确邮箱密码无法登录

解决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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 10:35:42