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

Django Rest Framework TokenAuthentication报错‘Invalid Token’求助

问题排查与解决方案

核心原因

DRF默认的TokenAuthentication和Django认证系统默认依赖username字段,但你的CustomUser用email替代了username作为用户唯一标识,若未正确配置相关参数,会导致Token验证时无法匹配到用户。

具体排查步骤与修复

1. 确认CustomUser模型配置正确

确保自定义用户模型明确指定email作为唯一标识字段:

# users/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    username = None  # 移除默认username字段
    email = models.EmailField(verbose_name='邮箱', unique=True)

    # 关键配置:指定email作为用户认证的唯一标识
    USERNAME_FIELD = 'email'
    # 若无需其他必填字段,设为空列表
    REQUIRED_FIELDS = []

    def __str__(self):
        return self.email

配置后运行迁移:

python manage.py makemigrations
python manage.py migrate

2. 检查登录视图的认证逻辑

登录时调用authenticate方法必须使用USERNAME_FIELD对应的参数名(即email),而非默认的username:

# users/views.py
from django.contrib.auth import authenticate
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authtoken.models import Token
from django.contrib.auth import get_user_model

User = get_user_model()

class LoginView(APIView):
    permission_classes = []  # 允许未登录访问

    def post(self, request):
        email = request.data.get('email')
        password = request.data.get('password')
        
        # 正确写法:使用USERNAME_FIELD动态获取参数名
        user = authenticate(
            request,
            **{User.USERNAME_FIELD: email, 'password': password}
        )

        if user and user.is_active:
            token, _ = Token.objects.get_or_create(user=user)
            return Response({'token': token.key})
        return Response({'detail': '无效的登录凭据'}, status=400)

3. 验证Token关联的用户状态

检查数据库中authtoken_token表对应的user_id是否存在,且该用户的is_active字段为True。若用户被标记为非活跃,Token验证会直接失败。

4. (可选)自定义TokenAuthentication类

若上述步骤仍未解决问题,可自定义认证类确保验证逻辑适配email字段:

# users/authentication.py
from rest_framework.authtoken.authentication import TokenAuthentication
from rest_framework.authtoken.models import Token
from django.contrib.auth import get_user_model

User = get_user_model()

class EmailTokenAuthentication(TokenAuthentication):
    def authenticate_credentials(self, key):
        try:
            # 关联查询用户,避免额外数据库请求
            token = Token.objects.select_related('user').get(key=key)
        except Token.DoesNotExist:
            return None

        # 验证用户是否活跃
        if not token.user.is_active:
            return None

        return (token.user, token)

然后在settings.py中替换默认认证类:

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'users.authentication.EmailTokenAuthentication',  # 自定义类
        # 'rest_framework.authentication.TokenAuthentication',  # 注释掉默认类
    ]
}

测试验证

在测试用例中确保请求头格式正确:

# goals/tests.py
from django.test import TestCase
from rest_framework.test import APIClient
from django.contrib.auth import get_user_model

User = get_user_model()

class TopicTestCase(TestCase):
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(email='test@example.com', password='123456')
        # 获取Token
        response = self.client.post('/api/login/', {'email': 'test@example.com', 'password': '123456'})
        self.token = response.data['token']

    def test_create_topic(self):
        # 设置请求头
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
        response = self.client.post('/api/goals/topics/', {'name': '测试主题'})
        self.assertEqual(response.status_code, 201)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 14:47:27