Django自定义用户模型调用authenticate()认证返回None问题
问题原因
authenticate()返回None是两个核心错误共同导致的:
- 密码存储错误:你数据库里的用户密码是明文存储的,没有做Django要求的加盐哈希处理。正常Django用户表的password字段存的是类似
pbkdf2_sha256$320000$随机盐$哈希值格式的字符串,从你的运行日志看,数据库取出的password和用户输入的明文完全一致,说明你创建用户时直接给password字段赋值了明文,没有走密码哈希逻辑。Django默认认证后端会把传入的明文密码做哈希后,和数据库存储的哈希值比对,存明文的情况下比对必然失败。 - 排查代码传参错误:你排查问题时,把从数据库取出的
x.password作为密码参数传给authenticate()。这个方法的password参数必须传用户输入的原始明文密码,内部会自动完成哈希校验,你传数据库存储的密码值,相当于把存储值当明文再做一次哈希去和库中值比对,永远不可能匹配。
另外你代码里导入的from django.contrib.auth.models import User是冗余代码,自定义用户模型继承AbstractUser后不需要再导入原生User模型,这个问题不会影响认证逻辑。
修复方法
- 修正用户创建逻辑:所有用户创建场景必须调用
set_password()方法处理明文密码,禁止直接给password字段赋明文字符串。代码创建用户参考如下写法,创建管理员账号直接用python manage.py createsuperuser命令即可,该命令会自动做密码哈希:
from users.models import UserDetails # 正确创建普通用户 user = UserDetails.objects.create( username='moradi', # 其余自定义字段按实际需求赋值 ) user.set_password('r123456m123456') # 传入明文,内部自动完成哈希加盐 user.save()
- 修正登录视图传参:
authenticate()必须传入表单接收的用户明文账号密码,不要传数据库查询出的password字段,同时建议传入request参数适配Django新版认证逻辑,修正后的登录视图代码:
def LoginPage(request): forms = LoginForm() if request.method == 'POST': forms = LoginForm(request.POST) if forms.is_valid(): username = forms.cleaned_data['Username'] password = forms.cleaned_data['Password'] # 传入request和用户输入的明文凭证做认证 user = authenticate(request, username=username, password=password) if user: login(request, user) return redirect('/stock') context = {'form': forms} return render(request, "users/login.html", context)
- 修复存量脏数据:之前明文存储密码的用户数据全部无法通过认证,可以直接删除后重新按正确方式创建,也可以逐一对存量用户调用
set_password()重新设置密码生成正确哈希值。
内容的提问来源于stack exchange,提问作者soroush khani
相关产品推荐
相关产品推荐

