将密码字段迁移到Django
我之前用过Django(1.2版本),总体来说我挺喜欢它的……特别是在快速启动一个全新项目方面表现得很好。不过这次我是在重写一个已有的系统,并把它迁移到Python/Django上。所以,我已经有一个MySQL数据库,里面有一个“users”表……这个表用MySQL的SHA1函数存储用户的密码(没有盐值等)。
在迁移的过程中,我打算修复一些数据建模上的缺陷,并且转到PostgreSQL数据库。
我真的很想使用django.contrib.auth,但我不太清楚该怎么做。我看过文档,知道可以把需要的用户信息和我额外的信息分开,然后放到UserProfile里。
但是,如何处理存储在MySQL数据库中的密码呢?
有没有人处理过这个问题?你们采取了什么方法?
3 个回答
1
最近版本的Django提供了一种处理没有加盐的旧密码的方法。你只需要把这个添加到你的设置文件里:
PASSWORD_HASHERS = (
...
'django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher',
)
1
你可以直接把密码放到 user_password
这个字段里 - 具体可以参考 Django 的文档。因为你没有盐值(salt),可以试试用 sha1$$password_hash
这种格式。我还没确认过用空盐值是否能行,但这可能是你在不修改 django.contrib.auth
或自己写认证后端的情况下,唯一能迁移的方法。
另外,你也可以给用户设置一个无效的密码(通常做法是把字段设置为 !
),然后引导他们使用找回密码的系统。
8
这是我让事情运转起来的方法。我创建了一个自定义的身份验证后端。注意:我使用电子邮件地址作为用户名。
这是我的代码:
from django.db.models import get_model
from django.contrib.auth.models import User
from hashlib import sha1
class MyUserAuthBackend(object):
def check_legacy_password(self, db_password, supplied_password):
return constant_time_compare(sha1(supplied_password).hexdigest(), db_password)
def authenticate(self, username=None, password=None):
""" Authenticate a user based on email address as the user name. """
try:
user = User.objects.get(email=username)
if '$' not in user.password:
if self.check_legacy_password(user.password, password):
user.set_password(password)
user.save()
return user
else:
return None
else:
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
""" Get a User object from the user_id. """
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
然后我在settings.py文件中添加了以下内容:
AUTHENTICATION_BACKENDS = (
'my_website.my_app.my_file.MyUserAuthBackend',
)
来自@Dougal的建议似乎是针对Django的下一个版本,而我用的是1.3.1,所以无法使用。不过,这看起来会是一个更好的解决方案。