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

如何解决Django Admin中自定义User模型密码明文存储问题?

解决Django自定义User模型Admin页面密码明文存储问题

我使用AbstractBaseUser定义了自定义User模型,其他功能均正常,但在Django原生Admin页面设置或更新用户密码时,密码会以明文形式存储而非哈希处理。相关代码如下:

models.py 代码

#models.py

class UserManager(BaseUserManager):
    def create_user(self, email, firstname, lastname, contact, password):
        if not email:
            raise ValueError('You must provide an email address')
        if not contact:
            raise ValueError('You must provide a contact number')
        
        email = self.normalize_email(email)
        user = self.model(email=email, firstname=firstname, lastname=lastname, contact=contact)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, firstname, lastname, contact, password):
        user = self.create_user(email, firstname, lastname, contact, password)
        user.is_admin = True
        user.save(using=self._db)
        return user

class Users(AbstractBaseUser):
    id = models.AutoField(primary_key=True)
    firstname = models.CharField(max_length=50)
    lastname = models.CharField(max_length=50)
    contact = models.CharField(max_length=10, unique=True)
    email = models.EmailField(unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    is_staff = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['firstname', 'lastname', 'contact']

    objects = UserManager()

    def save(self, *args, **kwargs):
        if not self.firstname or not self.lastname or not self.contact or not self.email or not self.password:
            raise ValueError('All required fields must be provided')
        super().save(*args, **kwargs)

    def __str__(self):
        return str(self.id) + '. ' + self.firstname + ' ' + self.lastname

    def has_perm(self, perm, obj=None):
        return self.is_admin

    def has_module_perms(self, app_label):
        return True
    
    class Meta:
        db_table = 'users'
        verbose_name_plural = 'Users'

admin.py 代码

#admin.py

from django.contrib import admin
from .models import *

admin.site.site_header = 'ROI Administration'
admin.site.site_title = 'ROI | Admin Panel'
admin.site.index_title = 'ROI Databases'

class UsersAdmin(admin.ModelAdmin):
    ordering = ['id']
    readonly_fields = ['id', 'created_at', 'last_login']
    fieldsets = [
        (None, {'fields': ['id', 'firstname', 'lastname', 'email', 'contact', 'last_login', 'created_at', 'is_staff', 'is_admin', 'password']}),
    ]

admin.site.register(Users, UsersAdmin)

问题原因

直接在Admin的fieldsets中包含password字段,会让Admin将输入的明文直接存入数据库,绕过了Django内置的密码哈希逻辑(即没有调用set_password方法生成哈希值)。

解决方案

方案1:移除Admin中直接显示的password字段

修改UsersAdmin的fieldsets,去掉password字段:

class UsersAdmin(admin.ModelAdmin):
    ordering = ['id']
    readonly_fields = ['id', 'created_at', 'last_login']
    fieldsets = [
        (None, {'fields': ['id', 'firstname', 'lastname', 'email', 'contact', 'last_login', 'created_at', 'is_staff', 'is_admin']}),
    ]

此时Admin页面不会显示密码输入框,如需修改密码,可使用Django自带的"修改密码"功能(通常在用户详情页的右上角)。

方案2:继承UserAdmin类(推荐)

Django的django.contrib.auth.admin.UserAdmin已经封装了密码哈希、密码确认等完整逻辑,让自定义Admin类继承它,自动获得正确的密码处理功能:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import Users

admin.site.site_header = 'ROI Administration'
admin.site.site_title = 'ROI | Admin Panel'
admin.site.index_title = 'ROI Databases'

class UsersAdmin(UserAdmin):
    ordering = ['id']
    readonly_fields = ['id', 'created_at', 'last_login']
    # 新增用户时的字段配置(包含密码确认框)
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'firstname', 'lastname', 'contact', 'password1', 'password2', 'is_staff', 'is_admin'),
        }),
    )
    # 编辑用户时的字段配置(隐藏密码,使用系统默认修改入口)
    fieldsets = (
        (None, {'fields': ('id', 'email', 'firstname', 'lastname', 'contact', 'last_login', 'created_at', 'is_staff', 'is_admin')}),
    )

admin.site.register(Users, UsersAdmin)

这样新增用户时会显示两个密码输入框(用于确认),保存时自动调用set_password生成哈希值;编辑用户时则隐藏密码字段,通过系统自带的入口修改密码。

方案3:修复自定义save方法的潜在问题

当前Users模型的save方法检查了self.password是否存在,但当通过Admin修改用户不修改密码时,self.password是已哈希的值,不会有问题。不过如果想更严谨,可以去掉密码的检查逻辑——因为create_user方法已经确保了密码必填,Admin的add_fieldsets也会强制输入密码,无需在save方法中重复验证:

def save(self, *args, **kwargs):
    # 去掉密码检查
    if not self.firstname or not self.lastname or not self.contact or not self.email:
        raise ValueError('All required fields must be provided')
    super().save(*args, **kwargs)

内容的提问来源于stack exchange,提问作者Mohit Aswani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 04:17:12