Apache2/Ubuntu服务器上Django用户Token认证失败问题排查
Token认证失败:"认证凭据未提供"故障排查与解决
问题背景
我正在开发对接安卓应用的Django REST Framework API,已配置端点、视图、模型、序列化器及settings.py,通过自定义命令创建了带Token的超级用户。但使用curl或Postman携带Token访问/usuario-actual/接口时,返回**"认证凭据未提供"**的错误,参照教程及GPT4协助后仍未解决。
现有配置代码
端点配置
from django.urls import path, include from .views import UserViewSet, UserProfileViewSet, CurrentUserView,LoginView urlpatterns = [ path('usuarios/', UserViewSet.as_view(), name='usuarios'), path('usuarios-perfil/', UserProfileViewSet.as_view(), name='usuarios-perfil'), path('usuario-actual/', CurrentUserView.as_view(), name='usuario-actual'), path('login/', LoginView.as_view(), name='login'), ]
视图配置
from rest_framework import generics from django.contrib.auth.models import User from .models import UserProfile from .serializers import UserSerializer, UserProfileSerializer from rest_framework.permissions import IsAuthenticated from django.contrib.auth import get_user_model 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 import logging logger = logging.getLogger(__name__) class UserViewSet(generics.ListCreateAPIView): queryset = User.objects.all() serializer_class = UserSerializer class UserProfileViewSet(generics.ListCreateAPIView): queryset = UserProfile.objects.all() serializer_class = UserProfileSerializer class CurrentUserView(generics.RetrieveAPIView): permission_classes = (IsAuthenticated,) serializer_class = UserSerializer def get_object(self): logger.info(self.request.META) logger.debug(f"CurrentUserView.get_object called for user {self.request.user}") return self.request.user def authenticate(email=None, password=None): UserModel = get_user_model() try: user = UserModel.objects.get(email=email) except UserModel.DoesNotExist: return None if user.check_password(password): return user # En tu LoginView class LoginView(APIView): def post(self, request, format=None): user = authenticate(email=request.data.get('email'), password=request.data.get('password')) if user is not None: token, created = Token.objects.get_or_create(user=user) return Response({'token': token.key}) else: return Response(status=401)
模型配置
from django.db import models from django.contrib.auth.models import User class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) is_premium = models.BooleanField(default=False) def __str__(self): return self.user.username
序列化器配置
from rest_framework import serializers from django.contrib.auth.models import User from .models import UserProfile class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ['username', 'is_staff'] class UserProfileSerializer(serializers.ModelSerializer): user = UserSerializer() class Meta: model = UserProfile fields = ['user', 'is_premium']
settings.py关键配置
INSTALLED_APPS = [ ... 'rest_framework', 'rest_framework.authtoken', ] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.TokenAuthentication', ] }
超级用户创建命令
from django.core.management.base import BaseCommand from django.contrib.auth.models import User from api_es.models import UserProfile from rest_framework.authtoken.models import Token # Asegúrate de que estás importando el modelo Token de aquí class Command(BaseCommand): help = 'Crea un nuevo usuario superusuario' def handle(self, *args, **options): username = 'lolo' password = 'AdGj2727' email = 'quizandguide@gmail.com' # Crear una nueva instancia de User y guardarla user = User(username=username, email=email, is_active=True, is_superuser=True, is_staff=True) user.set_password(password) user.save() # Crear una nueva instancia de UserProfile y guardarla user_profile = UserProfile(user=user, is_premium=True) user_profile.save() # Crear un token para el usuario token = Token.objects.create(user=user) self.stdout.write(self.style.SUCCESS('Superusuario creado exitosamente.'))
请求示例与错误返回
curl -H "Authorization: Token e65905ad748d67f127929c14d3a78b9de8300c51" https://football-quiz-and-guide.com/es/usuario-actual/
返回错误:
{"detail":"Las credenciales de autenticación no se proveyeron."}
翻译:认证凭据未提供
故障原因及解决办法
1. 自定义authenticate函数覆盖内置方法
视图中自定义的authenticate函数覆盖了Django自带的django.contrib.auth.authenticate方法,导致DRF的Token认证流程无法调用正确的认证逻辑。
解决办法:
重命名自定义认证函数,避免冲突:
# 修改函数名 def authenticate_by_email(email=None, password=None): UserModel = get_user_model() try: user = UserModel.objects.get(email=email) except UserModel.DoesNotExist: return None if user.check_password(password): return user # 在LoginView中调用修改后的函数 class LoginView(APIView): def post(self, request, format=None): user = authenticate_by_email(email=request.data.get('email'), password=request.data.get('password')) # 后续逻辑不变
2. 视图未显式指定认证类
全局默认认证类可能未被视图正确继承,需显式指定认证方式。
解决办法:
在CurrentUserView中添加authentication_classes配置:
class CurrentUserView(generics.RetrieveAPIView): permission_classes = (IsAuthenticated,) # 显式指定Token认证类 authentication_classes = ['rest_framework.authentication.TokenAuthentication'] serializer_class = UserSerializer def get_object(self): logger.info(self.request.META) logger.debug(f"CurrentUserView.get_object called for user {self.request.user}") return self.request.user
3. 请求头格式错误
curl命令中的转义引号会导致请求头解析失败,服务器无法识别Authorization字段。
解决办法:
修改curl命令,去掉转义引号:
# Linux/macOS环境 curl -H "Authorization: Token e65905ad748d67f127929c14d3a78b9de8300c51" https://football-quiz-and-guide.com/es/usuario-actual/ # Windows命令行环境 curl -H 'Authorization: Token e65905ad748d67f127929c14d3a78b9de8300c51' https://football-quiz-and-guide.com/es/usuario-actual/
4. 中间件配置缺失
缺少AuthenticationMiddleware会导致DRF无法处理用户认证会话。
解决办法:
检查settings.py的MIDDLEWARE配置,确保包含:
MIDDLEWARE = [ ... 'django.contrib.auth.middleware.AuthenticationMiddleware', ... ]
5. Token有效性验证
确认Token与用户正确关联,未被篡改或删除。
解决办法:
通过Django Shell验证Token:
from rest_framework.authtoken.models import Token token = Token.objects.get(key='e65905ad748d67f127929c14d3a78b9de8300c51') print(token.user.username) # 应输出'lolo'
若Token不存在,重新生成:
from django.contrib.auth.models import User from rest_framework.authtoken.models import Token user = User.objects.get(username='lolo') Token.objects.create(user=user)
内容的提问来源于stack exchange,提问作者user22258284
相关产品推荐
相关产品推荐

