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

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认证场景)都支持「用户名/邮箱+密码」的登录方式,可以额外添加自定义认证后端:

  1. 新建认证后端文件:
# 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
  1. 在项目配置文件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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 14:51:19