Django AuthenticationForm登录始终提示用户名密码错误排查
核心故障原因
注册用户时未对密码做哈希加密处理,直接将明文密码存入数据库。Django内置认证逻辑校验身份时,会将用户输入的明文密码按规则生成哈希值,与数据库中存储的密码哈希值做比对,数据库中存储明文密码时,二者永远无法匹配,因此表单始终返回用户名或密码错误提示。
问题出在自定义的UserRegisterForm保存逻辑:直接将明文密码赋值给用户模型的password字段后存入数据库,完全绕过了Django的密码哈希流程。
修复步骤
修正注册表单的用户保存逻辑
不要手动给password字段赋明文值,使用Django用户模型内置的set_password()方法处理密码哈希,或直接调用父类UserCreationForm的保存逻辑。修正后的forms.py代码如下:from django import forms from django.contrib.auth import get_user_model from django.contrib.auth.forms import UserCreationForm class UserRegisterForm(UserCreationForm): email = forms.EmailField() class Meta: model = get_user_model() fields = ['username', 'email', 'first_name'] def save(self, commit=True): user = super().save(commit=False) user.set_password(self.cleaned_data["password2"]) if commit: user.save() return user注:原代码中表单内多余的
authenticate、login导入、重复调用的self.clean()均可删除,is_valid()执行时已经完成了数据清洗逻辑,无需重复调用。简化登录视图逻辑
AuthenticationForm在执行is_valid()校验时已经内部完成了用户身份认证,无需手动重复调用authenticate方法,直接通过form.get_user()即可获取认证通过的用户对象。修正后的登录视图代码如下:from django.views.generic import CreateView, FormView from django.urls import reverse_lazy from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth import login class LoginView(FormView): form_class = AuthenticationForm template_name = "accounts/login.html" success_url = reverse_lazy("main:homepage") def form_valid(self, form): login(self.request, form.get_user()) return super().form_valid(form)注:原代码中重复导入
django.views.generic的冗余代码可直接删除,重写form_valid方法符合通用视图的设计规范,比手动重写post方法更不容易出错。清理旧的异常测试数据
修复代码前注册的测试用户,密码全部以明文形式存储,无法通过认证校验,需要进入后台将这类异常用户全部删除,重新走注册流程生成新用户后再测试登录功能。
注意事项
- 绝对不要直接给用户模型的
password字段赋值明文,创建用户时要么使用set_password()设置密码,要么调用objects.create_user()方法,该方法会自动完成密码哈希处理 - 使用Django内置的
AuthenticationForm时,不需要手动实现认证逻辑,表单内部已经封装了用户名密码校验、用户激活状态校验等全套逻辑,重复调用authenticate属于冗余操作 - 表单校验通过后不要重复调用
clean()方法,避免出现不可预期的逻辑错误
内容的提问来源于stack exchange,提问作者MakStef

