我正在使用一个遗留数据库,并创建了一个自定义用户模型。我正在设置注册和身份验证功能。我已经创建了用户管理器,在用户模型中,我为django添加了一些字段,如is_staff、is_active、date_joind。当我运行迁移时,遗留表仍然没有我在模型中添加的列。它真的应该改变遗留数据库吗?
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="剑">
您得到的错误是由于password
字段(以及last_login
字段)已经在AbstractBaseUser
中定义,如果您想使用Django验证,您必须使用该列才能使其正常工作。
您最好将遗留数据库转换为与Django auth系统兼容的形式。
您可能会面临的第二个问题是Django如何存储密码。由于你已经有了现有的数据,用户密码可能已经以某种形式存储在数据库中。你可以用三种不同的方法来修复它。
1.将您的密码转换为Django支持的格式
如果您的遗留系统以纯文本形式存储所有密码,或者使用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就知道它不能用于登录。用户仍然可以使用密码重置电子邮件功能重置密码。
由于您提供了旧系统中现有密码哈希的示例,我将添加一个与您的情况更相关的示例。
密码开头的CCD_ 9表示CCD_。但是,它在旧系统中的确切计算方式很难确定,必须在旧系统的代码中查找,因为bcrypt
可能被用于其他算法或任何其他密码操作方案之上。
但是由于bcrypt
本身足够安全,您不需要将这种类型的散列封装在另一层散列中。如果你以前的系统不是简单地通过bcrypt,或者先通过sha256,然后通过bcrypto,那么可能需要实现你自己的密码哈希器。
如果你很难通过阅读旧系统的代码来确定它,或者你根本做不到,你可以通过尝试错误来检查它。要检查Django中内置的两个变体,首先通过安装django[bcrypt]
来安装所需的库。
接下来,确保要测试的两个hasher都在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$$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算法的输入永远不会超过该限制,无论用户提供的实际密码长度如何。