Django登录接口返回Invalid email or password错误排查与修复求助
问题现象
实现账号登录时,系统返回错误:{"error": "Invalid email or password"},但已通过管理后台确认邮箱与密码均正确。
核心问题分析
从代码来看,主要有以下几个关键问题导致认证失败:
自定义User模型未正确实现密码哈希
你的User模型直接用CharField存储password,但Django的authenticate函数是基于哈希密码进行验证的。如果密码未经过哈希存储(比如直接赋值而非调用set_password),验证必然失败。认证参数不匹配默认后端规则
默认的Django认证后端(ModelBackend)仅识别username和password参数,而你的LoginView中使用了authenticate(request, email=email, password=password),email参数无法被默认后端识别,导致无法找到用户。自定义User模型未配置登录标识字段
未设置USERNAME_FIELD = 'email',认证后端无法将邮箱作为登录的唯一标识,即便传入邮箱作为username参数,也无法正确匹配用户。LoginSerializer未被实际调用
LoginView的post方法直接从request.data取值,没有使用定义好的LoginSerializer做验证,相当于白写了序列化器的逻辑。
修复步骤
1. 修正自定义User模型
继承AbstractBaseUser和PermissionsMixin,实现密码哈希功能,并设置邮箱为登录标识:
from django.db import models from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin class UserManager(BaseUserManager): def create_user(self, email, password=None, **extra_fields): if not email: raise ValueError('必须填写邮箱') email = self.normalize_email(email) user = self.model(email=email, **extra_fields) user.set_password(password) # 自动哈希密码 user.save(using=self._db) return user def create_superuser(self, email, password=None, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) return self.create_user(email, password, **extra_fields) class User(AbstractBaseUser, PermissionsMixin): email = models.EmailField(max_length=255, unique=True) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) USERNAME_FIELD = 'email' # 设置邮箱为登录标识 objects = UserManager()
2. 配置自定义User模型
在项目的settings.py中添加:
AUTH_USER_MODEL = '你的应用名.User' # 替换为实际的应用名称
3. 修复LoginView的认证逻辑
选择以下两种方式之一:
方式一:使用LoginSerializer处理验证
def post(self, request): serializer = self.serializer_class(data=request.data) serializer.is_valid(raise_exception=True) user = serializer.validated_data['user'] login(request, user) token, created = Token.objects.get_or_create(user=user) return Response({'token': token.key}, status=status.HTTP_200_OK)
方式二:直接修正authenticate参数
def post(self, request): email = request.data.get('email') password = request.data.get('password') # 用username参数传入邮箱,匹配USERNAME_FIELD设置 user = authenticate(request, username=email, password=password) if user is not None: login(request, user) token, created = Token.objects.get_or_create(user=user) return Response({'token': token.key}, status=status.HTTP_200_OK) return Response({'error': 'Invalid email or password'}, status=status.HTTP_401_UNAUTHORIZED)
4. 确保密码哈希存储
无论是通过admin后台还是代码创建用户,都要确保密码经过哈希:
- 代码创建用户时,必须调用
user.set_password(password)而非直接赋值user.password = password - admin后台创建用户时,Django会自动处理哈希,无需额外操作
验证修复
- 重新迁移数据库:
python manage.py makemigrations+python manage.py migrate - 创建新用户(或重置现有用户密码,确保密码被哈希)
- 测试登录接口,确认返回正常的token
内容的提问来源于stack exchange,提问作者Zero Xefon

