Django REST Framework自定义用户模型的邮箱登录验证失败问题
嘿,我看你已经卡了48小时了,先别着急,咱们一点点排查问题。你的核心需求是用邮箱+密码登录获取Token,但现在一直返回401,咱们先从代码里的几个关键问题入手:
一、自定义认证后端(EmailBackend)里的明显错误
你看这段代码:
except UserModel.DoesNotExist: UserModel().set_password(password)
这行代码完全是无效的——当用户不存在时,你创建了一个临时的User实例并设置了密码,但既没保存也没返回任何值,直接跳过了后续逻辑,这会导致authenticate最终返回None,触发“Invalid Email or Password”。
修复方法:当用户不存在时直接返回None,因为不存在的用户肯定无法通过验证:
except UserModel.DoesNotExist: return None # 用户不存在,直接返回None
另外,还有个小细节:你的UserModel最好用Django提供的get_user_model()来获取当前配置的用户模型,避免硬编码。修改你的EmailBackend开头:
from django.contrib.auth import get_user_model UserModel = get_user_model()
二、检查Settings.py里的关键配置
这两步必须配置,否则Django还是会用默认的用户模型和认证后端:
- 告诉Django使用你的自定义用户模型:
AUTH_USER_MODEL = "你的应用名.CustomUser" # 比如"accounts.CustomUser"
- 指定使用你的自定义认证后端:
AUTHENTICATION_BACKENDS = [ "你的应用名.backends.EmailBackend", # 比如"accounts.backends.EmailBackend" # 如果需要保留默认的用户名登录,可以加上下面这行,否则只留上面一行即可 "django.contrib.auth.backends.ModelBackend", ]
三、登录序列化器(LoginSerializer)的小优化
你的LoginSerializer用了ModelSerializer,但password字段默认不是write_only的,虽然不影响输入,但为了规范和避免后续问题,最好显式设置:
class LoginSerializer(serializers.ModelSerializer): email = serializers.EmailField() password = serializers.CharField(write_only=True) # 显式设为只写 class Meta: model = CustomUser fields = ['email','password']
四、确认用户创建时密码是否正确哈希
如果你的CustomUserManager没有正确实现create_user方法,创建用户时密码会是明文存储,导致check_password验证失败。检查你的CustomUserManager:
class CustomUserManager(BaseUserManager): def create_user(self, email, password=None, **extra_fields): if not email: raise ValueError("The Email must be set") email = self.normalize_email(email) user = self.model(email=email, **extra_fields) user.set_password(password) # 这一步必须有,用来哈希密码 user.save() 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)
如果你的Manager里没有set_password这一步,赶紧加上,然后重新创建测试用户(之前创建的用户密码是明文,需要重新创建)。
五、最后检查LoginAPIView里的逻辑
你的LoginAPIView逻辑基本没问题,但可以加个小调试:在authenticate之后打印一下user的值,看看是None还是有值,帮助定位问题:
user = authenticate(request, username=email, password=password) print("Authenticated user:", user) # 调试用,生产环境去掉
按照上面的步骤逐一排查修改,应该就能解决你的登录问题了!
备注:内容来源于stack exchange,提问作者lem thee stallion

