Django用户密码哈希算法定制:外部数据库SHA512密码迁移后认证失败问题求助
问题排查与解决方案
你的自定义密码哈希器存在几个关键问题导致认证失败,我来逐一拆解并给出修正方案:
核心问题分析
encode方法返回类型错误:你返回的是hashlib.sha512对象,而非数据库存储的十六进制字符串,导致验证时无法和数据库中的密码字符串做有效对比。verify方法逻辑错误:你直接用encode返回的hash对象和数据库中的字符串进行比较,类型不匹配必然失败。- 未兼容Django密码格式:Django的密码存储遵循
algorithm$salt$hash的格式,而你迁移的密码是纯SHA512十六进制字符串,需要兼容这种场景。
修正后的自定义哈希器代码
import hashlib from django.contrib.auth.hashers import BasePasswordHasher, mask_hash from django.utils.crypto import constant_time_compare from django.utils.translation import gettext_noop as _ class CustomPasswordHasher(BasePasswordHasher): algorithm = 'sha512' def salt(self): # 原密码无salt,返回空字符串 return '' def encode(self, password, salt): # 计算密码的SHA512十六进制摘要 hash_hex = hashlib.sha512(password.encode()).hexdigest() # 遵循Django密码格式:algorithm$salt$hash return f"{self.algorithm}${salt}${hash_hex}" def verify(self, password, encoded): # 兼容两种密码格式:迁移的纯哈希字符串 / Django格式字符串 if '$' in encoded: # 拆分Django格式的密码段 algorithm, salt, hash_hex = encoded.split('$', 2) # 如果算法不匹配,直接返回验证失败 if algorithm != self.algorithm: return False else: # 处理迁移过来的纯SHA512哈希字符串 salt = '' hash_hex = encoded # 计算输入密码的SHA512哈希 computed_hash = hashlib.sha512(password.encode()).hexdigest() # 用constant_time_compare防止时序攻击 return constant_time_compare(hash_hex, computed_hash) def safe_summary(self, encoded): # 适配两种格式的密码摘要展示 if '$' in encoded: algorithm, salt, hash_hex = encoded.split('$', 2) else: algorithm = self.algorithm salt = '' hash_hex = encoded return { _('algorithm'): algorithm, _('salt'): mask_hash(salt, show=0), # 无salt,隐藏全部 _('hash'): mask_hash(hash_hex, show=6), # 显示前6位哈希 } def must_update(self, encoded): # 建议:验证通过后自动升级为更安全的哈希算法(如PBKDF2) return '$' not in encoded
额外优化建议
- 自动升级密码哈希:上面的
must_update方法会标记迁移过来的纯哈希密码为需要更新,当用户成功登录后,Django会自动使用PASSWORD_HASHERS列表中后续的更安全哈希器(比如PBKDF2)重新加密密码并保存到数据库,提升安全性。 - 测试验证:你可以创建一个测试用户,将其
password字段直接设置为你提供的示例哈希值,然后尝试登录,验证是否能成功认证。
配置确认
你的settings.py中PASSWORD_HASHERS的配置是正确的,确保CustomPasswordHasher排在最前面,这样Django会优先使用它来验证密码。
内容的提问来源于stack exchange,提问作者I. Simonov
相关产品推荐
相关产品推荐

