django新密码使用password_hashers[0]指定的算法,旧密码仅在用户登录或改密时自动升级;若新注册用户仍用旧算法,常见原因是目标哈希器未置顶、路径拼写错误、依赖未安装或服务未重启。

直接改 PASSWORD_HASHERS 列表顺序就能让新密码用新算法,但旧密码不会自动升级——必须等用户下次登录或主动重设时才重新哈希。
为什么改了 PASSWORD_HASHERS 但新注册用户还是老算法?
常见错误是只改了列表内容,却没把目标算法放在第一位。Django 只认 PASSWORD_HASHERS[0] 作为默认存储算法。
- 确保你要用的类(比如
Argon2PasswordHasher)是列表第一个元素 - 检查拼写和路径:必须是完整模块路径,如
'django.contrib.auth.hashers.Argon2PasswordHasher',少一个点或大小写错都会静默失败(Django 启动不报错,但 fallback 到默认) - 确认依赖已安装:
argon2-cffi不在 Django 默认依赖里,pip install django[argon2]必须执行 - 重启 Django 进程(开发用
runserver要 Ctrl+C 再启动;生产环境需 reload uwsgi/gunicorn)
check_password() 能验证所有旧密码吗?
能,前提是旧密码对应算法仍在 PASSWORD_HASHERS 列表中,且位置靠前到能被匹配上。
- Django 验证时会解析密码字符串开头的算法标识(如
$argon2i$或$pbkdf2_sha256$),然后在PASSWORD_HASHERS中找对应类 - 如果旧密码是
BCryptSHA256加密的,但你删掉了BCryptSHA256PasswordHasher,验证就会抛ValueError - 列表顺序不影响验证逻辑,只影响存储;验证是“按需查找”,不是“从头遍历”
如何安全地批量升级存量用户密码?
不能直接批量 rehash,因为明文密码不可得;只能借用户登录/改密流程渐进升级。
- 在自定义登录视图或
authenticate()后钩子里判断:如果user.password开头不是当前PASSWORD_HASHERS[0].algorithm,就调用user.set_password(raw_password)再user.save() - 避免在
check_password()外裸写make_password(),否则可能绕过 hasher 的 salt 和迭代参数 - 注意并发风险:两个请求同时登录同一用户,可能产生竞态写入;加个
select_for_update()更稳妥 - 上线前用测试账号走一遍全流程,确认密码字段更新后格式变成新算法(比如从
$pbkdf2_sha256$...变成$argon2id$...)
最易忽略的一点:PASSWORD_HASHERS 是运行时配置,修改后必须重启服务;而且它直接影响 User.set_password()、create_user() 等所有密码写入路径——别只测表单提交,漏掉管理命令或 API 接口里的用户创建逻辑。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











