我正在尝试创建一个模型,我可以在其中存储其他应用程序的用户名和密码。如何在Django中设置密码字段,使其在管理员中不是纯文本?提前谢谢。
答案 0 :(得分:27)
作为@mlissner suggested,auth.User
模型是一个值得关注的好地方。如果您选中source code,则会看到password
字段为CharField
。
password = models.CharField(_('password'), max_length=128, help_text=_("Use
'[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
User
模型也有set_password
方法。
def set_password(self, raw_password):
import random
algo = 'sha1'
salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
hsh = get_hexdigest(algo, salt, raw_password)
self.password = '%s$%s$%s' % (algo, salt, hsh)
您可以从此方法中获取有关创建密码并保存密码的一些线索。
答案 1 :(得分:5)
我认为您无法解密以与普通django用户密码类似的方式存储的加密密码。部分安全性是它们不可拆卸。
答案 2 :(得分:4)
不幸的是,这个问题没有一个简单的答案,因为它取决于您尝试进行身份验证的应用程序,而且还取决于您希望密码字段的安全程度。
如果您的Django应用程序将使用密码对另一个需要发送明文密码的应用程序进行身份验证,那么您的选项是:
如果您使用的是Django的内置用户模型,则可以使用Django用户密码作为主密码。这意味着您需要将该主密码保留在内存中,这可能会使您难以完成某些操作,例如重新启动服务器或运行负载均衡的冗余服务器。
幸运的是,许多现代应用程序使用基于密钥而非基于密码的访问令牌系统以另一种方式支持这一点。引导用户完成在两个应用程序之间建立链接的过程,并且在后台,应用程序生成密钥以永久地或以定义的过期日期相互验证。
例如,Facebook支持这种模式,并且他们有关于它如何运作的大量文档:Facebook Developers: Access Tokens and Types
使用[OAuth 2.0](http://tools.ietf.org/html/draft-ietf-oauth-v2-12)设法与Facebook建立链接后,您可能会发现更容易向其他人添加链接使用相同协议的应用程序。
答案 3 :(得分:3)
你最好的选择(我知道)是深入研究django代码中的代码,看看它是如何完成的。我记得,他们生成一个salted哈希,以便纯文本值永远不会存储在任何地方,而是哈希和盐。
如果你进入django安装,然后四处寻找像哈希和盐这样的词,你应该很快找到它。对于模糊的回答感到抱歉,但也许它会让你走上正确的道路。
答案 4 :(得分:2)
如果您需要可逆密码字段,可以使用以下内容:
from django.db import models
from django.core.exceptions import ValidationError
from django.conf import settings
from os import urandom
from base64 import b64encode, b64decode
from Crypto.Cipher import ARC4
from django import forms
PREFIX = u'\u2620'
class EncryptedCharField(models.CharField):
__metaclass__ = models.SubfieldBase
SALT_SIZE = 8
def __init__(self, *args, **kwargs):
self.widget = forms.TextInput
super(EncryptedCharField, self).__init__(*args, **kwargs)
def get_internal_type(self):
return 'TextField'
def to_python(self, value):
if not value:
return None
if isinstance(value, basestring):
if value.startswith(PREFIX):
return self.decrypt(value)
else:
return value
else:
raise ValidationError(u'Failed to encrypt %s.' % value)
def get_db_prep_value(self, value, connection, prepared=False):
return self.encrypt(value)
def value_to_string(self, instance):
encriptado = getattr(instance, self.name)
return self.decrypt(encriptado) if encriptado else None
@staticmethod
def encrypt(plaintext):
plaintext = unicode(plaintext)
salt = urandom(EncryptedCharField.SALT_SIZE)
arc4 = ARC4.new(salt + settings.SECRET_KEY)
plaintext = u"%3d%s%s" % (len(plaintext), plaintext, b64encode(urandom(256-len(plaintext))))
return PREFIX + u"%s$%s" % (b64encode(salt), b64encode(arc4.encrypt(plaintext.encode('utf-8-sig'))))
@staticmethod
def decrypt(ciphertext):
salt, ciphertext = map(b64decode, ciphertext[1:].split('$'))
arc4 = ARC4.new(salt + settings.SECRET_KEY)
plaintext = arc4.decrypt(ciphertext).decode('utf-8-sig')
return plaintext[3:3+int(plaintext[:3].strip())]
加密部分基于https://djangosnippets.org/snippets/1330/上的代码段,我只是将其转换为字段模型,添加了utf-8支持并添加了前缀作为Django's silly use of to_python()的解决方法