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

Django自定义认证支持邮箱/用户名登录却无法登录,求原因排查

Django 邮箱/用户名双登录认证失败排查

问题背景

开发Django认证项目,目标实现用户通过邮箱或用户名登录。自定义用户模型、管理器及认证后端代码如下,但创建超级用户流程正常,登录认证始终失败。

自定义用户模型

class LinksUser(AbstractBaseUser, PermissionsMixin):
    objects = LinksUserManager()
    full_name = models.CharField(max_length=200, blank=False)
    email = models.EmailField(_("email address"), unique=True)
    username = models.CharField(_("username"), unique=True, max_length=20)
    is_active = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)

    USERNAME_FIELD = "username"
    EMAIL_FIELD = "email"
    REQUIRED_FIELDS = ['email']

    def __str__(self):
        return self.email

自定义用户管理器

class LinksUserManager(BaseUserManager):
    """
    Custom user-model manager where the email and username are identifiers
    for authentication
    """

    def create_user(self, email, username, password, **extra_fields):
        if not username:
            raise ValueError('Username must be set')
        if not email:
            raise ValueError('Email must be set')
        email = self.normalize_email(email)
        user = self.model(username=username, email=email)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, username, email, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError(_('Superuser must have is_staff=True.'))
        if extra_fields.get('is_superuser') is not True:
            raise ValueError(_('Superuser must have is_superuser=True.'))

        return self.create_user(username, email, password, **extra_fields)

自定义认证后端

from .models import LinksUser

class LinkUserBackend(object):
    """
    Model backend that attempts to allow a user to log in with either the
    username or the email address
    """

    def authenticate(self, request, username=None, password=None):
        try:
            user = LinksUser.objects.get(email=username)
        except LinksUser.DoesNotExist:
            try:
                user = LinksUser.objects.get(username=username)
            except LinksUser.DoesNotExist:
                return None
        if user.check_password(password):
            return user

可能的原因及解决方法

1. 自定义认证后端未配置到settings

Django默认使用django.contrib.auth.backends.ModelBackend,未将自定义后端加入配置的话,系统不会用它处理登录请求。

解决方法:在settings.py中添加:

AUTHENTICATION_BACKENDS = [
    'your_app_name.backends.LinkUserBackend',  # 替换为实际的后端路径
]

2. 认证后端缺少get_user方法

Django要求认证后端必须实现get_user方法,用于登录后获取用户对象,缺失会导致认证失败。

解决方法:在LinkUserBackend中补充方法:

def get_user(self, user_id):
    try:
        return LinksUser.objects.get(pk=user_id)
    except LinksUser.DoesNotExist:
        return None

3. 创建超级用户时参数顺序错误

create_superuser调用create_user时,将username和email的传参顺序搞反了,导致用户数据存储错误(邮箱字段存了用户名,用户名字段存了邮箱),登录时无法匹配正确用户。

解决方法:修正create_superuser中的调用代码:

return self.create_user(email, username, password, **extra_fields)

4. 用户is_active状态为False

自定义用户模型中is_active默认是False,未激活的用户无法登录。超级用户创建时也未显式设置is_active=True,导致超级用户同样处于未激活状态。

解决方法:

  • 在create_user方法中默认设置激活状态:
    def create_user(self, email, username, password, **extra_fields):
        # ... 原有代码
        extra_fields.setdefault('is_active', True)
        user = self.model(username=username, email=email, **extra_fields)
        # ... 原有代码
    
  • 或在create_superuser中单独设置:
    def create_superuser(self, username, email, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)  # 添加这一行
        # ... 原有代码
    

5. 认证后端继承错误

自定义后端应继承django.contrib.auth.backends.BaseBackend而非直接继承object,否则可能引发兼容性问题。

解决方法:修改后端继承:

from django.contrib.auth.backends import BaseBackend

class LinkUserBackend(BaseBackend):
    # ... 原有代码

内容的提问来源于stack exchange,提问作者Juan Monster

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 20:23:09