Django 运行 遗留数据库迁移
Django running migrations on legacy database
我正在使用遗留数据库并创建了自定义用户模型。我正在努力设置注册和身份验证功能。我创建了用户管理器,在用户模型中有一些我为 django 添加的字段,如 is_staff、is_active、date_joined。当我 运行 迁移时,遗留 table 仍然没有我在模型中添加的列。它真的应该改变遗留数据库吗?
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, blank=True, 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, email, psword, **kwargs):
user = self.create_user(email, psword, **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 就知道它不能用于登录。用户仍然可以使用密码重设电子邮件功能重设密码。
由于您提供了旧系统中现有密码散列的示例,我将添加一个与您的案例更相关的示例。
a
开头的密码表示bcrypt
。但是它在你的旧系统中是如何精确计算的不能轻易确定并且必须在你的旧系统的代码中查找,因为 bcrypt
可以在另一种算法或任何其他密码操作方案之上使用.
但由于 bcrypt
本身足够安全,您不需要将这种类型的哈希值包装在另一层哈希值中。如果您以前的系统不是简单地通过 bcrypt 或首先通过 sha256 然后通过 bcrypt 传递密码,则可能需要实现您自己的密码哈希器。
如果你看旧系统的代码很难判断,或者根本不会判断,可以试错一下。要检查 Django 内置的 2 个变体,首先通过安装 django[bcrypt]
.
安装所需的库
接下来,确保在 Django 设置中启用了您要测试的两个哈希器,最好将您的 PASSWORD_HASHERS
设置设置为:
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
]
接下来,在您的旧系统中创建一个新用户帐户(或获取您知道密码的任何现有帐户),从中复制密码哈希并在其前面添加一个 bcrypt$
前缀(此散列的最终形式应在此修改后以 bcrypt$a$
开头。注意双 $
符号)。使用准备好的哈希,使用 ./manage.py shell
打开管理控制台并执行:
from django.contrib.auth.hashers import check_password
check_password('your known password', 'your_modified_password_hash')
如果此函数 returns 正确,则您的旧系统使用纯 bcrypt。如果结果为 False,请将 bcrypt
替换为 bcrypt_sha256
以检查第二个密码哈希并再次执行 check_password
。如果成功,您的旧系统会在将密码传递给 bcrypt 之前用 sha256 包装密码。
如果任一测试成功,您需要做的就是根据您的测试结果分别为旧系统的所有密码哈希值添加一个 bcrypt$
或 bcrypt_sha256$
前缀。如果 none 有效,您需要查看旧系统代码以确定确切的哈希方法。
如果你的旧系统使用 bcrypt 和 sha256(你需要对旧数据做的就是添加 bcrypt_sha256$
前缀),我建议将 PASSWORD_HASHERS
恢复为默认值.如果你想继续使用旧的散列方法,你可以将你的方法移动到 PASSWORD_HASHERS
列表的顶部,这样 Django 将使用这个方法创建任何新帐户并且它不会将旧密码散列更改为另一个用户使用密码登录时的方法。
关于 BCryptSHA256PasswordHasher
的附注。 Django 在默认密码哈希套件中使用它,因为普通 bcrypt 有最大密码长度限制。首先通过 SHA256 传递它可确保无论用户提供的实际密码长度如何,bcrypt 算法的输入都不会超过该限制。
我正在使用遗留数据库并创建了自定义用户模型。我正在努力设置注册和身份验证功能。我创建了用户管理器,在用户模型中有一些我为 django 添加的字段,如 is_staff、is_active、date_joined。当我 运行 迁移时,遗留 table 仍然没有我在模型中添加的列。它真的应该改变遗留数据库吗?
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, blank=True, 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, email, psword, **kwargs):
user = self.create_user(email, psword, **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 就知道它不能用于登录。用户仍然可以使用密码重设电子邮件功能重设密码。
由于您提供了旧系统中现有密码散列的示例,我将添加一个与您的案例更相关的示例。
a
开头的密码表示bcrypt
。但是它在你的旧系统中是如何精确计算的不能轻易确定并且必须在你的旧系统的代码中查找,因为 bcrypt
可以在另一种算法或任何其他密码操作方案之上使用.
但由于 bcrypt
本身足够安全,您不需要将这种类型的哈希值包装在另一层哈希值中。如果您以前的系统不是简单地通过 bcrypt 或首先通过 sha256 然后通过 bcrypt 传递密码,则可能需要实现您自己的密码哈希器。
如果你看旧系统的代码很难判断,或者根本不会判断,可以试错一下。要检查 Django 内置的 2 个变体,首先通过安装 django[bcrypt]
.
接下来,确保在 Django 设置中启用了您要测试的两个哈希器,最好将您的 PASSWORD_HASHERS
设置设置为:
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
]
接下来,在您的旧系统中创建一个新用户帐户(或获取您知道密码的任何现有帐户),从中复制密码哈希并在其前面添加一个 bcrypt$
前缀(此散列的最终形式应在此修改后以 bcrypt$a$
开头。注意双 $
符号)。使用准备好的哈希,使用 ./manage.py shell
打开管理控制台并执行:
from django.contrib.auth.hashers import check_password
check_password('your known password', 'your_modified_password_hash')
如果此函数 returns 正确,则您的旧系统使用纯 bcrypt。如果结果为 False,请将 bcrypt
替换为 bcrypt_sha256
以检查第二个密码哈希并再次执行 check_password
。如果成功,您的旧系统会在将密码传递给 bcrypt 之前用 sha256 包装密码。
如果任一测试成功,您需要做的就是根据您的测试结果分别为旧系统的所有密码哈希值添加一个 bcrypt$
或 bcrypt_sha256$
前缀。如果 none 有效,您需要查看旧系统代码以确定确切的哈希方法。
如果你的旧系统使用 bcrypt 和 sha256(你需要对旧数据做的就是添加 bcrypt_sha256$
前缀),我建议将 PASSWORD_HASHERS
恢复为默认值.如果你想继续使用旧的散列方法,你可以将你的方法移动到 PASSWORD_HASHERS
列表的顶部,这样 Django 将使用这个方法创建任何新帐户并且它不会将旧密码散列更改为另一个用户使用密码登录时的方法。
关于 BCryptSHA256PasswordHasher
的附注。 Django 在默认密码哈希套件中使用它,因为普通 bcrypt 有最大密码长度限制。首先通过 SHA256 传递它可确保无论用户提供的实际密码长度如何,bcrypt 算法的输入都不会超过该限制。