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

Django项目实现超级用户邮箱/普通用户账号的差异化认证方案

解决Django中不同用户类型使用不同登录字段的认证问题

我明白你现在遇到的困扰了——想在同一个Django项目里实现超级用户用邮箱创建/登录,普通用户用专属账号登录,但直接修改USERNAME_FIELD不仅没解决普通用户的登录问题,连createsuperuser命令都开始要求输入账号,完全偏离了预期。别慌,咱们用自定义用户模型+自定义认证后端的方式就能搞定这个需求。

第一步:创建自定义用户模型

首先要替换默认的User模型,因为我们需要新增account字段作为普通用户的登录凭证,同时保留邮箱作为超级用户的核心标识。

在你的app目录下的models.py中添加以下代码:

from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models
import random
import string

class CustomUserManager(BaseUserManager):
    # 普通用户创建逻辑:必须传入account,邮箱可选(根据你的需求调整)
    def create_user(self, account, email=None, password=None, **extra_fields):
        if not account:
            raise ValueError("账号字段不能为空")
        email = self.normalize_email(email)
        user = self.model(account=account, email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    # 超级用户创建逻辑:只要求输入邮箱,自动生成唯一账号
    def create_superuser(self, 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("超级用户必须设置is_staff=True")
        if extra_fields.get('is_superuser') is not True:
            raise ValueError("超级用户必须设置is_superuser=True")
        
        # 自动生成唯一账号:用邮箱前缀+随机字符串
        prefix = email.split('@')[0]
        while True:
            suffix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=4))
            account = f"{prefix}_{suffix}"
            if not self.model.objects.filter(account=account).exists():
                break
        
        return self.create_user(account=account, email=email, password=password, **extra_fields)

class CustomUser(AbstractBaseUser, PermissionsMixin):
    account = models.CharField(max_length=50, unique=True, verbose_name="用户账号")
    email = models.EmailField(max_length=255, unique=True, verbose_name="邮箱")
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

    objects = CustomUserManager()

    # 把USERNAME_FIELD设为email,这样createsuperuser会提示输入邮箱
    USERNAME_FIELD = 'email'
    # 不需要额外必填字段,留空即可
    REQUIRED_FIELDS = []

第二步:编写自定义认证后端

默认的认证后端只会用USERNAME_FIELD指定的字段(也就是我们刚设的email)来验证登录,所以需要自定义后端,让它同时支持邮箱和账号两种登录凭证。

在app目录下新建backends.py,添加以下代码:

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from django.db.models import Q

CustomUser = get_user_model()

class CustomAuthBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            # 先尝试用邮箱匹配(超级用户登录用这个)
            user = CustomUser.objects.get(email=username)
        except CustomUser.DoesNotExist:
            # 邮箱匹配失败,再尝试用账号匹配(普通用户登录用这个)
            try:
                user = CustomUser.objects.get(account=username)
            except CustomUser.DoesNotExist:
                # 两种都匹配不到,返回None表示认证失败
                return None
        
        # 验证密码是否正确
        if user.check_password(password):
            return user
        return None

第三步:配置settings.py

告诉Django使用我们的自定义用户模型和认证后端:

# 指定自定义用户模型,格式为"app名.模型名"
AUTH_USER_MODEL = 'your_app_name.CustomUser'

# 添加自定义认证后端,保留默认后端以防万一
AUTHENTICATION_BACKENDS = [
    'your_app_name.backends.CustomAuthBackend',
    'django.contrib.auth.backends.ModelBackend',
]

第四步:处理普通用户注册逻辑

根据你的需求,普通用户注册后要自动分配账号,所以在注册视图里可以自动生成唯一的account值:

from django.contrib.auth import get_user_model
from django.shortcuts import render, redirect
from django.contrib.auth.hashers import make_password
import random
import string

CustomUser = get_user_model()

def register(request):
    if request.method == 'POST':
        email = request.POST.get('email')
        password = request.POST.get('password')
        
        # 生成唯一账号的逻辑,和superuser创建时类似
        prefix = email.split('@')[0]
        while True:
            suffix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=4))
            account = f"{prefix}_{suffix}"
            if not CustomUser.objects.filter(account=account).exists():
                break
        
        # 创建普通用户
        user = CustomUser.objects.create(
            account=account,
            email=email,
            password=make_password(password)
        )
        # 注册成功后跳转到登录页
        return redirect('login')
    return render(request, 'register.html')

第五步:迁移数据库并测试

完成以上配置后,执行数据库迁移命令:

python manage.py makemigrations
python manage.py migrate

现在测试两个核心场景:

  1. 创建超级用户:执行python manage.py createsuperuser,会提示输入邮箱,符合你的需求,账号会自动生成。
  2. 普通用户登录:用注册时分配的账号和密码登录,自定义认证后端会自动匹配账号完成认证;超级用户用邮箱登录也能正常通过。

为什么之前的方法不行?

直接修改USERNAME_FIELD为account会让Django全局使用账号作为默认登录字段,导致createsuperuser命令也要求输入账号,同时普通用户的登录逻辑没有适配多字段验证,所以才会出现登录失败的问题。通过自定义认证后端,我们扩展了认证逻辑,让系统能同时识别邮箱和账号两种登录凭证,完美匹配你的需求。

内容的提问来源于stack exchange,提问作者Kenaz Odhiambo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:15:30