Django Simple JWT如何配置支持邮箱密码登录获取Token
问题原因
Simple JWT默认的Token获取接口强制以username作为用户身份标识字段完成认证校验,没有内置适配邮箱登录的逻辑,直接提交email+password参数会触发「username为必填字段」的校验报错。
修改步骤
1. 调整自定义用户模型,添加邮箱唯一约束
你当前的CustomUser继承自AbstractUser,默认的email字段没有唯一约束,直接用邮箱作为登录标识会出现多用户匹配的问题,先修改用户模型代码:
# 对应用户模型存放文件,一般为users/models.py from django.contrib.auth.models import AbstractUser from django.db import models class CustomUser(AbstractUser): # 给邮箱字段加唯一约束,确保每个邮箱对应一个账号 email = models.EmailField(unique=True) # 创建超级管理员时要求填写邮箱 REQUIRED_FIELDS = ["email"]
修改完成后依次执行以下命令生成并应用数据库迁移:
python manage.py makemigrations python manage.py migrate
注意:如果项目已经存在存量用户数据,执行迁移前需要先清理重复的邮箱记录,否则会触发数据库唯一约束报错。
2. 自定义JWT登录序列化器,替换默认认证逻辑
在用户应用下新建/修改序列化器文件,继承Simple JWT默认的TokenObtainPairSerializer,重写字段规则和校验逻辑:
# 对应序列化器存放文件,一般为users/serializers.py from rest_framework import serializers from rest_framework_simplejwt.serializers import TokenObtainPairSerializer from django.contrib.auth import get_user_model User = get_user_model() class EmailTokenObtainPairSerializer(TokenObtainPairSerializer): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 删除默认必填的username字段 del self.fields["username"] # 新增email必填字段 self.fields["email"] = serializers.EmailField(required=True) def validate(self, attrs): email = attrs.get("email") password = attrs.get("password") # 按邮箱查询对应用户 try: user = User.objects.get(email=email) except User.DoesNotExist: raise serializers.ValidationError("邮箱或密码错误") # 校验密码正确性 if not user.check_password(password): raise serializers.ValidationError("邮箱或密码错误") # 校验账号是否处于可用状态 if not user.is_active: raise serializers.ValidationError("当前账号已被禁用") # 注入用户对象,复用原生token生成逻辑 attrs["user"] = user return super().validate(attrs)
3. 修改路由配置,替换默认Token接口的序列化器
找到项目中配置JWT接口的路由文件,将默认/api/token/接口绑定的序列化器替换为我们自定义的类:
# 对应路由配置文件,一般为项目根urls.py或api/urls.py from django.urls import path from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView from users.serializers import EmailTokenObtainPairSerializer urlpatterns = [ # 替换原有的/api/token/路由配置 path("api/token/", TokenObtainPairView.as_view(serializer_class=EmailTokenObtainPairSerializer), name="token_obtain_pair"), path("api/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), # 其余业务路由保持不变 ]
可选扩展配置:如果希望全站(包括Admin后台、其他DRF认证场景)都支持「用户名/邮箱+密码」的登录方式,可以额外添加自定义认证后端:
- 新建认证后端文件:
# users/backends.py from django.contrib.auth.backends import ModelBackend from django.contrib.auth import get_user_model from django.db.models import Q User = get_user_model() class EmailOrUsernameAuthBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): # 传入的username参数既可以匹配用户名,也可以匹配邮箱 try: user = User.objects.get(Q(username=username) | Q(email=username)) except User.DoesNotExist: return None if user.check_password(password) and self.user_can_authenticate(user): return user return None
- 在项目配置文件settings.py中添加配置:
AUTHENTICATION_BACKENDS = [ "users.backends.EmailOrUsernameAuthBackend" ]
验证方式
配置完成后即可使用邮箱+密码请求Token接口,示例请求如下:
curl \ -X POST \ -H "Content-Type: application/JSON" \ -d '{"email": "your_account@example.com", "password": "your_account_password"}' \ http://localhost:8000/api/token/
请求成功后会正常返回access和refresh token,不会再提示username字段必填。
内容的提问来源于stack exchange,提问作者user18923197
相关产品推荐
相关产品推荐

