
django 默认会在用户密码变更后自动失效旧会话,导致用户登出;解决方法是调用 `update_session_auth_hash()` 保留当前登录状态。
在 Django 中,当用户密码被修改(例如通过自定义视图 psswdReset),框架出于安全考虑会主动使所有基于旧密码的会话失效——这是默认行为,并非 Bug。其原理在于:Django 的认证中间件会比对当前 session 中存储的哈希签名与用户 password 字段的哈希值。一旦 user.password 被直接更新(如 user.password = make_password(...)),而未同步更新 session 的认证标识,下次请求时 AuthenticationMiddleware 将检测到不一致,强制将 request.user 置为 AnonymousUser,即“会话被冲刷”。
您当前的代码存在两个关键问题:
- 未调用 update_session_auth_hash():这是最核心的修复点;
- 手动赋值 user.password 并保存:虽可行,但绕过了 Django 用户模型的安全更新逻辑,易遗漏信号或钩子。
✅ 正确做法如下(推荐使用 set_password() + update_session_auth_hash()):
from django.contrib.auth import update_session_auth_hash
from django.contrib import messages
def psswdReset(request):
if request.method == 'POST':
new_psswd = request.POST.get('new_psswd')
psswd = request.POST.get('psswd')
# 验证原密码正确性
if not request.user.check_password(psswd):
messages.error(request, 'Current password is incorrect.')
return render(request, 'User/userPsswdReset.html')
# 安全设置新密码(自动哈希)
request.user.set_password(new_psswd)
request.user.save()
# ? 关键:更新 session 认证哈希,防止登出
update_session_auth_hash(request, request.user)
messages.success(request, 'Password changed successfully!')
return render(request, 'User/userPsswdReset.html')
return render(request, 'User/userPsswdReset.html')? 注意事项:
- update_session_auth_hash(request, user) 必须在 user.save() 之后调用,且 user 必须是当前已认证的用户实例(通常为 request.user);
- 不要使用 check_password(psswd, request.user.password) 手动比对——应直接调用 request.user.check_password(),它更安全且兼容自定义密码 hasher;
- 若使用 CustomUser 模型,请确保其继承自 AbstractBaseUser 或 AbstractUser,并正确实现了 set_password() 方法;
- 该机制仅影响当前用户的活跃会话,其他设备/浏览器的会话仍会被安全地注销(符合预期)。
? 总结:密码修改后会话丢失是 Django 的主动安全防护机制,而非缺陷。只需在保存新密码后调用 update_session_auth_hash(),即可在保障安全性的同时提供无缝的用户体验。










