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

如何在Django自定义用户模型中实现无哈希算法的密码字段与认证功能

实现Django自定义用户模型的明文密码认证

我明白你的需求——因为要适配遗留数据库,所以得让Django的自定义用户模型跳过密码哈希,直接用明文存储和认证。虽然这确实有安全风险,但既然你的应用不对外公开,那咱们就来实现这个方案。

下面是修改后的完整代码,核心是重写AbstractBaseUser中处理密码的两个关键方法:

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.utils import timezone
from django.contrib.auth.hashers import UNUSABLE_PASSWORD_PREFIX

class AccountManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, email, name, phone, password, **extra_fields):
        values = [email, name, phone]
        field_value_map = dict(zip(self.model.REQUIRED_FIELDS, values))
        for field_name, value in field_value_map.items():
            if not value:
                raise ValueError('The {} value must be set'.format(field_name))
        email = self.normalize_email(email)
        user = self.model(
            email=email,
            name=name,
            phone=phone,
            **extra_fields
        )
        user.set_password(password)  # 这里还是调用set_password,但我们已经重写了逻辑
        user.save(using=self._db)
        return user

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

    def create_superuser(self, email, name, phone, 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(email, name, phone, password, **extra_fields)

class Account(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    name = models.CharField(max_length=150)
    phone = models.CharField(max_length=50)
    date_of_birth = models.DateField(blank=True, null=True)
    picture = models.ImageField(blank=True, null=True)
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    date_joined = models.DateTimeField(default=timezone.now)
    last_login = models.DateTimeField(null=True)

    objects = AccountManager()

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

    def get_full_name(self):
        return self.name

    def get_short_name(self):
        return self.name.split()[0]

    # 重写set_password:直接存储明文,跳过哈希
    def set_password(self, raw_password):
        if raw_password is None:
            self.set_unusable_password()
        else:
            # 直接赋值明文,不做任何哈希处理
            self.password = raw_password
            self._password = raw_password  # 保留Django内部的密码跟踪属性

    # 重写check_password:直接对比明文,不验证哈希
    def check_password(self, raw_password):
        if self.password is None:
            return False
        # 保留不可用密码的处理逻辑(比如用户未设置密码的情况)
        if self.password.startswith(UNUSABLE_PASSWORD_PREFIX):
            return False
        # 直接对比输入的明文和数据库存储的密码
        return raw_password == self.password

关键修改说明

  • 重写set_password:原来的父类方法会对明文密码做哈希处理,现在我们直接把明文赋值给password字段,同时保留_password属性(Django用来跟踪密码是否修改的内部字段)。如果传入None,依然调用set_unusable_password()保持原有逻辑。
  • 重写check_password:默认方法会尝试用哈希算法验证密码,现在我们直接对比输入的明文和数据库中存储的明文。同时保留了对“不可用密码”的处理,确保和原有逻辑一致。
  • 管理器无需修改:原来的创建用户逻辑中调用的user.set_password(password)会自动使用我们重写后的方法,直接存储明文。

额外注意事项

  1. 即使应用不对外公开,也要严格控制数据库的访问权限,避免明文密码泄露。
  2. 遗留数据库中的明文密码可以直接迁移过来,不需要做任何转换。
  3. 现有登录逻辑(比如使用authenticate函数)不需要修改,会自动调用我们重写的check_password方法完成认证。

内容的提问来源于stack exchange,提问作者Mohammad Esmaeili

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 21:43:15