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

Django自定义用户模型登录失败:手机号与密码错误排查求助

Django手机号登录失败及密码加密问题解决

问题背景

使用手机号和密码登录Django系统时失败,前端提示invalid phone number data;Admin面板查看用户时,密码字段显示Invalid password or unknown encryption algorithm。已自定义User模型,集成ghasedakpack实现OTP注册,配置了多认证后端。

核心问题分析

  1. 密码未加密存储:注册时直接通过User.objects.create()存入明文密码,未使用Django内置的密码加密机制,导致authenticate无法验证密码,同时Admin无法识别密码格式。
  2. 自定义User模型冗余字段:手动定义了password字段,覆盖了AbstractBaseUser提供的加密密码字段,破坏了Django的密码管理逻辑。
  3. OTP流程用户创建逻辑缺陷:OtpView中使用get_or_create可能创建无密码用户,导致后续登录异常。

分步解决方案

1. 修复自定义User模型

移除手动定义的password字段,依赖AbstractBaseUser内置的加密密码字段:

class User(AbstractBaseUser):
    email = models.EmailField(
        verbose_name="آدرس ایمیل",
        max_length=257,
        unique=True,
        blank=True,
        null=True
    )
    fullname = models.CharField(max_length=50, verbose_name="نام کامل")
    phone = models.CharField(max_length=11, unique=True, verbose_name="تلفن")
    # 移除冗余的password字段
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    objects = UserManager()

    USERNAME_FIELD = "phone"
    REQUIRED_FIELDS = []

    # 其余方法保持不变...

2. 实现正确的UserManager

确保自定义UserManager包含create_user方法,自动处理密码加密:

from django.contrib.auth.base_user import BaseUserManager

class UserManager(BaseUserManager):
    def create_user(self, phone, password=None, **extra_fields):
        if not phone:
            raise ValueError('必须提供手机号')
        user = self.model(phone=phone, **extra_fields)
        user.set_password(password)  # 自动加密密码
        user.save(using=self._db)
        return user

    def create_superuser(self, phone, password=None, **extra_fields):
        extra_fields.setdefault('is_admin', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_admin') is not True:
            raise ValueError('超级用户必须设置is_admin=True')
        return self.create_user(phone, password, **extra_fields)

3. 修复注册视图的用户创建逻辑

替换User.objects.create()为create_user方法,确保密码加密:

# RegisterView的post方法中,替换原用户创建代码
user = User.objects.create_user(
    phone=cd['phone'],
    fullname=cd['fullname'],
    email=cd['email'],
    password=cd['password'],
)

4. 修复OTP验证视图的登录逻辑

注册流程已创建用户,OTP验证时直接获取已存在用户,避免创建无密码用户:

def post(self, request):
    token = request.GET.get('token')
    form = OtpForm(request.POST)
    if form.is_valid():
        cd = form.cleaned_data
        otp_obj = Otp.objects.filter(code=cd['code'], token=token).first()
        if otp_obj:
            # 直接获取注册时创建的用户
            user = User.objects.get(phone=otp_obj.phone)
            # 指定手机号认证后端登录
            login(request, user, backend='account.authentication.PhoneAuthenticationBackend')
            otp_obj.delete()
            return redirect('home:home')
        else:
            form.add_error('code', '验证码无效或已过期')
    else:
        form.add_error('code', '验证码格式错误')

    return render(request, 'account/otp.html', {'form': form})

5. 确保手机号认证后端正确实现

验证PhoneAuthenticationBackend逻辑正确,能通过手机号和密码验证用户:

from django.contrib.auth.backends import BaseBackend
from .models import User

class PhoneAuthenticationBackend(BaseBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            # 用username参数匹配手机号
            user = User.objects.get(phone=username)
            if user.check_password(password):
                return user
        except User.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

6. 清理错误数据

对之前明文存储密码的用户,需更新密码为加密格式(或删除后重新注册):

# 进入Django shell执行:python manage.py shell
from account.models import User

# 示例:更新指定用户密码
user = User.objects.get(phone='你的测试手机号')
user.set_password('用户的原密码')
user.save()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 09:54:55