0

0

Django自定义用户模型:Admin登录失效与正确实现指南

DDD

DDD

发布时间:2025-11-07 12:41:01

|

675人浏览过

|

来源于php中文网

原创

Django自定义用户模型:Admin登录失效与正确实现指南

本文深入探讨了在django中实现自定义用户模型时,超级用户无法登录admin面板的常见问题。通过分析`abstractbaseuser`和`permissionsmixin`的内部机制,指出了因重复定义密码字段和验证方法导致的冲突。文章提供了正确的模型实现方式,强调了利用django内置认证功能的重要性,确保自定义用户模型能够安全、高效地与admin系统集成。

引言:自定义用户模型的必要性

Django提供了一个强大的内置用户模型,但在许多实际应用中,开发者可能需要根据业务需求扩展或替换它。例如,使用电子邮件而非用户名作为主要认证凭据,或者添加额外的用户属性。Django通过AUTH_USER_MODEL设置和AbstractBaseUser、PermissionsMixin等基类,为自定义用户模型提供了灵活的框架。然而,不正确的实现方式可能导致一系列问题,其中最常见的就是超级用户无法登录Django Admin面板。

Django Admin登录失效的常见问题

当开发者尝试构建一个基于电子邮件的自定义用户模型,并继承自AbstractBaseUser和PermissionsMixin时,可能会遇到超级用户在Django Admin登录时反复提示“请输入正确的电子邮件和密码”的错误,即使确认凭据无误。这个问题通常发生在以下场景:

  1. 自定义用户模型 (Customers):

    from django.utils import timezone
    from django.db import models
    from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, UserManager
    
    # 自定义管理器
    class CustomerManager(UserManager):
        def _create_user(self, email, password, **extra_fields):
            if not email:
                raise ValueError('Customers must have an email address')
            user = self.model(
                email=email,
                **extra_fields
            )
            user.set_password(password) # 使用Django内置方法设置密码
            user.save(using=self._db)
            return user
    
        def create_user(self, email=None, password=None, **extra_fields):
            extra_fields.setdefault('is_superuser', False)
            extra_fields.setdefault('is_staff', False)
            return self._create_user(email, password, **extra_fields)
    
        def create_superuser(self, name, last_name, email, phone, password, **kwargs):
            kwargs.setdefault('is_superuser', True)
            kwargs.setdefault('is_staff', True)
            return self._create_user(email, password, **kwargs)
    
    # 自定义用户模型
    class Customers (AbstractBaseUser, PermissionsMixin):
        name = models.CharField(max_length=20)
        last_name = models.CharField(max_length=20)
        email = models.EmailField(blank=False, unique=True)
        phone = models.CharField(max_length=15)
        password = models.CharField(max_length=20) # 错误:重复定义密码字段
    
        is_active = models.BooleanField(default=True)
        is_staff = models.BooleanField(default=False)
        is_superuser = models.BooleanField(default=False) # 错误:重复定义is_superuser
    
        date_joined = models.DateTimeField(default=timezone.now)
        last_login = models.DateTimeField(blank=True, null=True)
        objects = CustomerManager()
    
        USERNAME_FIELD = 'email'
        EMAIL_FIELD = 'email'
        REQUIRED_FIELDS = ['name', 'last_name', 'phone']
    
        class Meta:
            verbose_name = 'Customer'
            verbose_name_plural = 'Customers'
    
        def get_full_name(self):
            return self.name + ' ' + self.last_name
    
        def get_short_name(self):
            return self.name
    
        def check_password(self, password): # 错误:覆盖了AbstractBaseUser的密码验证方法
            return self.password == password
  2. settings.py配置:

    AUTH_USER_MODEL = 'customers.Customers'

尽管在自定义管理器中使用了user.set_password(password)来设置密码,但由于模型内部的错误定义,导致登录验证失败。

问题根源分析

上述问题的核心在于对AbstractBaseUser和PermissionsMixin这两个基类提供的核心功能的重复定义和不当覆盖。

  1. password 字段的重复定义:AbstractBaseUser基类已经包含了对用户密码的管理,它会在内部维护一个经过哈希处理的密码字段。当我们在Customers模型中再次定义password = models.CharField(max_length=20)时,实际上是创建了一个新的、未经Django哈希机制处理的明文字段。虽然CustomerManager中的set_password方法会调用AbstractBaseUser的set_password来正确哈希密码并存储,但当Django尝试验证密码时,它可能会错误地使用我们自定义的明文password字段,或者在后续的数据库操作中造成混淆。

  2. is_superuser 字段的重复定义:PermissionsMixin基类已经提供了is_superuser和is_staff等权限相关的布尔字段。重复定义is_superuser = models.BooleanField(default=False)同样会导致不必要的冗余和潜在的冲突。

    居然设计家
    居然设计家

    居然之家和阿里巴巴共同打造的家居家装AI设计平台

    下载
  3. check_password 方法的覆盖:AbstractBaseUser提供了一个健壮且安全的check_password方法,用于验证用户输入的密码与数据库中存储的哈希密码是否匹配。通过自定义def check_password(self, password): return self.password == password,我们完全覆盖了Django内置的安全验证逻辑。这个自定义方法直接比较了用户输入的密码与模型中自定义的明文password字段,这不仅不安全(因为密码以明文形式存储或被错误处理),也绕过了Django的哈希密码验证机制,从而导致登录失败。

解决方案:精简与信任Django内置功能

解决此问题的关键在于移除自定义用户模型中与AbstractBaseUser和PermissionsMixin功能重复的字段和方法,充分信任并利用Django提供的内置认证机制。

修正后的 Customers 模型:

from django.utils import timezone
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, UserManager

# CustomerManager 保持不变,因为它正确地使用了 set_password
class CustomerManager(UserManager):
    def _create_user(self, email, password, **extra_fields):
        if not email:
            raise ValueError('Customers must have an email address')
        user = self.model(
            email=email,
            **extra_fields
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email=None, password=None, **extra_fields):
        extra_fields.setdefault('is_superuser', False)
        extra_fields.setdefault('is_staff', False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, name, last_name, email, phone, password, **kwargs):
        kwargs.setdefault('is_superuser', True)
        kwargs.setdefault('is_staff', True)
        return self._create_user(email, password, **kwargs)

# 修正后的自定义用户模型
class Customers (AbstractBaseUser, PermissionsMixin):
    name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=20)
    email = models.EmailField(blank=False, unique=True)
    phone = models.CharField(max_length=15)

    # 移除 password 字段,AbstractBaseUser 已提供

    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    # 移除 is_superuser 字段,PermissionsMixin 已提供

    date_joined = models.DateTimeField(default=timezone.now)
    last_login = models.DateTimeField(blank=True, null=True)
    objects = CustomerManager()

    USERNAME_FIELD = 'email'
    EMAIL_FIELD = 'email'
    REQUIRED_FIELDS = ['name', 'last_name', 'phone']

    class Meta:
        verbose_name = 'Customer'
        verbose_name_plural = 'Customers'

    def get_full_name(self):
        return self.name + ' ' + self.last_name

    def get_short_name(self):
        return self.name

    # 移除 check_password 方法,AbstractBaseUser 已提供安全的验证逻辑

修改说明:

  1. 移除 password 字段: AbstractBaseUser 已经内置了处理密码的机制,包括存储哈希值。我们无需再显式定义一个 password 字段。
  2. 移除 is_superuser 字段: PermissionsMixin 已经提供了 is_superuser 和 is_staff 字段,用于管理用户权限和Admin访问。
  3. 移除 check_password 方法: AbstractBaseUser 提供了安全的 check_password 方法,它能够正确地验证用户输入的密码与数据库中存储的哈希密码。覆盖此方法会导致安全漏洞和认证失败。

通过这些修改,自定义用户模型将正确地继承和利用Django内置的认证和权限管理功能,从而使超级用户能够顺利登录Admin面板。

注意事项与最佳实践

  • 密码管理: 始终使用 user.set_password('your_password') 来设置用户密码,而不是直接赋值给 user.password。set_password 方法会负责对密码进行哈希处理。
  • 权限管理: AbstractBaseUser 和 PermissionsMixin 共同提供了完整的用户认证和权限管理框架。除非有非常特殊的需求,否则应尽量避免覆盖或重新实现它们的核心方法和字段。
  • 数据库迁移: 在修改模型后,务必运行 python manage.py makemigrations 和 python manage.py migrate 来更新数据库结构。如果现有数据与新模型不兼容,可能需要手动处理或清空数据库重新创建超级用户。
  • 自定义管理器: UserManager 是处理用户创建和管理的关键。在自定义管理器中,确保 create_user 和 create_superuser 方法正确地调用了 _create_user 并最终使用了 user.set_password()。

总结

在Django中构建自定义用户模型是一项常见的任务,但它要求开发者对AbstractBaseUser和PermissionsMixin的内部工作原理有清晰的理解。当遇到超级用户无法登录Admin面板的问题时,首先应检查自定义模型中是否存在对这些基类功能的重复定义或不当覆盖,特别是关于密码字段和验证方法。通过精简模型,移除冗余字段和方法,并充分利用Django内置的安全认证机制,可以确保自定义用户模型不仅功能完善,而且安全可靠地与Django Admin系统集成。

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

707

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

624

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

734

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

616

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1234

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

547

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

573

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

695

2023.08.11

苹果官网入口直接访问
苹果官网入口直接访问

苹果官网直接访问入口是https://www.apple.com/cn/,该页面具备0.8秒首屏渲染、HTTP/3与Brotli加速、WebP+AVIF双格式图片、免登录浏览全参数等特性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

10

2025.12.24

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 0.6万人学习

Django 教程
Django 教程

共28课时 | 2.4万人学习

SciPy 教程
SciPy 教程

共10课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号