问题描述
我正在使用旧数据库并创建了自定义用户模型。我正在努力设置注册和身份验证功能。我创建了用户管理器,在用户模型中,我为 django 添加了一些字段,如 is_staff、is_active、date_joined。当我运行迁移时,旧表仍然没有我在模型中添加的列。它真的应该改变旧数据库吗?
class TbUser(AbstractBaseUser,PermissionsMixin):
id = models.CharField(primary_key=True,max_length=40)
usname = models.CharField(max_length=40,blank=True,null=True,unique=True)
psword = models.CharField(max_length=255,null=True)
# added columns
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.Now)
objects = TbUserManager()
USERNAME_FIELD = 'usname'
required_FIELDS = []
class Meta:
managed = False
db_table = 'tb_user'
django.db.utils.OperationalError: (1054,"UnkNown column 'tb_user.password' in 'field list'")
虽然用户管理器看起来像这样
class TbUserManager(BaseUserManager):
def create_user(self,email,psword=None,**kwargs):
if not email:
raise ValueError('Users must have a valid email address.')
if not kwargs.get('usname'):
raise ValueError('Users must have a valid username.')
user = self.model(
email=self.normalize_email(email),usname=kwargs.get('usname')
)
user.psword(psword)
user.save()
return user
def create_superuser(self,psword,**kwargs):
user = self.create_user(email,**kwargs)
user.is_superuser = True
user.save()
return user
我真的不知道错误在哪里找到了 tb_user.password
因为我已将所有重命名为 psword
如果您需要一些详细信息,请随时询问。
编辑:
我发现密码错误是由于模型命名psword造成的,有没有办法告诉django这是密码字段?例如:USER_PASSWORD='psword'
解决方法
您得到的错误是由于 password
字段(以及 last_login
字段)已经在 AbstractBaseUser
中定义,并且如果您想使用 Django 验证,您必须使用该列才能使其正常工作。
最好将旧数据库转换为与 Django 身份验证系统兼容的形式。
您可能会遇到的第二个问题是 how Django stores passwords。由于您已经拥有现有数据,因此用户密码可能已经以某种形式存储在数据库中。您可以通过 3 种不同的方式修复它。
1.将您的密码转换为 Django 支持的形式。
如果您的旧系统以纯文本或使用任何方法 already supported by Django 存储所有密码,您只需将密码转换为其中之一即可。
我建议只使用一个默认启用的散列器,或者从下面描述的解决方案中选择另一种解决方案。
2.创建您自己的密码哈希器,将您现有的密码封装在 Django 中默认启用的密码中。
例如,如果您的旧方法使用 md5 对密码进行散列,您可以编写自己的密码散列器,它将采用现有的编码密码并将其放入现有密码散列器之一(如 django.contrib.auth.hashers.PBKDF2PasswordHasher
) .
然后,您的设置可能如下所示:
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher','django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher','django.contrib.auth.hashers.Argon2PasswordHasher','django.contrib.auth.hashers.BCryptSHA256PasswordHasher','your_auth_app.hashers.PBKDF2LegacyPasswordHasher',]
通过该配置,Django 将知道如何验证用户提供的密码,但它也会在用户登录时自动将其密码转换为列表中的第一个哈希器(因为 Django 将有权访问明文密码,它可以自己编码)
3.强制每个用户在访问新系统之前重置密码。
只需在旧密码前面添加一个 !
字符,Django 就知道它不能用于登录。用户仍然可以使用密码重置电子邮件功能重置密码。
由于您提供了旧系统中现有密码哈希的示例,因此我将添加一个与您的情况更相关的示例。
密码开头的$2a
表示 bcrypt
。但是它在旧系统中的计算方式无法轻易确定,必须在旧系统的代码中查找,因为 bcrypt
可能用于另一种算法或任何其他密码操作方案之上.
但是由于 bcrypt
本身已经足够安全,您不需要将这种类型的散列包装在另一层散列中。如果您以前的系统不是简单地通过 bcrypt 或首先通过 sha256 然后通过 bcrypt,则可能需要实现您自己的密码散列器。
如果你通过阅读旧系统的代码很难确定,或者你根本做不到,你可以通过试错来检查。要检查 Django 内置的 2 个变体,首先通过安装 django[bcrypt]
来安装所需的库。
接下来,确保在 Django 设置中启用了您要测试的两个哈希器,最好将 PASSWORD_HASHERS
设置设置为:
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher','django.contrib.auth.hashers.BCryptPasswordHasher',]
接下来,在您的旧系统中创建一个新用户帐户(或获取任何您知道密码的现有用户帐户),从中复制密码哈希并在其前面添加一个 bcrypt$
前缀(最后一个在此修改后,此散列的形式应以 bcrypt$$2a$
开头。注意双 $
符号)。使用准备好的散列,使用 ./manage.py shell
打开管理控制台并执行:
from django.contrib.auth.hashers import check_password
check_password('your known password','your_modified_password_hash')
如果此函数返回 True,则您的旧系统使用普通 bcrypt。如果结果为 False,请将 bcrypt
替换为 bcrypt_sha256
以检查第二个密码哈希并再次执行 check_password
。如果成功,您的旧系统会在将密码传递给 bcrypt 之前用 sha256 包装密码。
如果任一测试成功,您需要做的就是分别添加 bcrypt$
或 bcrypt_sha256$
前缀,具体取决于您的测试结果,到旧系统的所有密码哈希。如果它们都不起作用,您需要查看旧系统代码以确定确切的散列方法。
如果您的旧系统使用带有 sha256 的 bcrypt(并且您需要对旧数据做的就是添加 bcrypt_sha256$
前缀),我建议将 PASSWORD_HASHERS
恢复为默认值。如果您想继续使用旧的散列方法,您可以将您的方法移到 PASSWORD_HASHERS
列表的顶部,这样 Django 将使用此方法创建任何新帐户,并且不会将旧密码散列更改为另一个用户使用密码登录时的方法。
关于 BCryptSHA256PasswordHasher
的旁注。 Django 在默认密码哈希器套件中使用它,因为普通 bcrypt 具有最大密码长度限制。首先通过 SHA256 确保 bcrypt 算法的输入永远不会超过该限制,无论用户提供的实际密码长度如何。