You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中check_password返回True但authenticate返回None的问题排查

问题:Django登录接口authenticate返回None但check_password正常的问题

我用Django内置功能编写登录接口的单元测试,账号创建接口功能已确认正常,但在登录视图中,check_password()函数返回True,authenticate()却返回None。请问能否安全使用check_password()替代?若不能,该如何修改代码以使用authenticate()?

注:已确认用户is_active字段为True。

相关代码

accounts.py

class Account(AbstractUser):
    username = models.CharField(max_length=150, unique=True, null=False, default='')
    password = models.CharField(max_length=100)
    ...

    REQUIRED_FIELDS = ['username', 'password']

    class Meta:
        app_label = 'accounts'
        db_table = 'accounts_account'

    objects = models.Manager()

test_login.py

def test_login(self):
    # Create account
    request_create = self.factory.post('/accounts/account',
                                       self.request_data,
                                       content_type='application/json')
    view = AccountView.as_view()
    response_create = view(request_create)

    # Login account
    request_login = self.factory.post('/accounts/login',
                                      self.request_data,
                                      content_type='application/json')

    view = LoginView.as_view()
    response = view(request_login)

views.py

class LoginView(View):

    def post(self, request):
        r = json.loads(request.body)
        username = r.get('username')
        password = r.get('password')
        cp = check_password(password, Account.objects.get(username=username).password)
        user = authenticate(username=username, password=password)

解答

1. 绝对不能用check_password()替代authenticate()

check_password()仅做密码哈希值的匹配验证,但authenticate()是Django认证系统的核心入口,它会处理:

  • 验证用户is_active状态(你已确认正常,但还有其他逻辑)
  • 支持多认证后端的扩展逻辑
  • 触发user_logged_in等认证相关信号
  • 后续调用login()函数必须依赖authenticate()返回的用户对象,直接用check_password()通过的用户登录,会跳过认证系统的安全校验,存在风险且不符合Django规范。

2. 修复authenticate()返回None的问题

问题根源在你的Account模型:手动重新定义了password字段,覆盖了AbstractUser自带的password字段逻辑。

AbstractUser的password字段会自动处理密码的哈希存储,而你手动定义的CharField会直接存储明文(或未正确哈希的内容)。这就导致:

  • check_password()能匹配是因为直接拿输入和存储的内容做对比
  • authenticate()会用标准的密码哈希验证逻辑,自然匹配失败返回None

具体修复步骤:

  1. 删除Account模型中手动定义的password字段:
class Account(AbstractUser):
    username = models.CharField(max_length=150, unique=True, null=False, default='')
    # 移除这一行:password = models.CharField(max_length=100)
    ...
  1. 确保账号创建视图AccountView中,用Django提供的set_password()方法设置密码,而不是直接赋值:
# 示例:AccountView创建用户的核心逻辑
username = r.get('username')
password = r.get('password')
user = Account(username=username)
user.set_password(password)  # 该方法会自动哈希密码
user.save()
  1. 清空测试数据库后重新运行测试,确保新创建的用户密码是正确哈希存储的。

完成以上修改后,authenticate(username=username, password=password)就能正常返回用户对象了。


内容的提问来源于stack exchange,提问作者ang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 22:20:46