You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
注册

Django REST Framework令牌位置疑问及登录视图代码审核

关于DRF令牌传递逻辑的解释及登录视图代码审核

一、令牌相关逻辑澄清

你混淆了两个不同场景的信息传递规则:

  1. 登录请求(提交账号密码):必须将邮箱、密码放在请求体(body)中,这是HTTP安全规范的要求——敏感信息放在URL(GET参数)会被日志、浏览器历史、代理服务器捕获,存在泄露风险,POST请求用body传递是标准且安全的做法,DRF文档提到的“放在body里”指的是这个场景。
  2. 后续认证请求(使用令牌):DRF官方推荐的TokenAuthentication是将令牌放在HTTP请求头中,格式为Authorization: Token <你的令牌字符串>。这是REST API的通用规范:身份认证信息不属于业务数据,放在请求头里更符合语义,也能让DRF的认证组件统一处理。

至于部分教程将令牌放在body里,大多是为了新手入门简化流程,或者是自定义的认证逻辑,但这并非最佳实践,推荐遵循官方规范使用请求头传递令牌。

二、登录视图代码审核及优化

你的代码逻辑整体是正确的,但存在一些细节问题和可优化点:

现有问题:

  • 类名不符合PEP8规范(Python类名应使用大驼峰)
  • 响应中冗余的status字段(HTTP响应状态码已经能标识状态,无需重复返回)
  • 调试用的print语句应该移除(生产环境会污染日志)
  • 登录失败的响应格式不符合DRF默认错误格式,不利于前端统一处理
  • 未确保认证后端支持邮箱登录(默认authenticate方法使用username字段,若你的用户模型用邮箱作为登录凭证,需要自定义认证后端)

优化后的代码:

# 导入必要模块(若未导入)
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.authentication import authenticate
from rest_framework.authtoken.models import Token
from .serializers import LoginSerializer  # 序列化器类名同样遵循大驼峰

class UserLogin(APIView):
    serializer_class = LoginSerializer

    def post(self, request, format=None):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)

        email = serializer.validated_data['email']
        password = serializer.validated_data['password']
        # 确保你的认证后端支持邮箱认证,否则需自定义
        user = authenticate(request, username=email, password=password)
        
        if user:
            token, created = Token.objects.get_or_create(user=user)
            # 返回规范的响应结构,包含必要用户信息和令牌
            return Response({
                "user": {
                    "id": user.id,
                    "email": user.email
                },
                "token": token.key
            }, status=status.HTTP_200_OK)
        else:
            # 返回DRF标准格式的错误信息
            return Response(
                {"detail": "邮箱或密码错误"},
                status=status.HTTP_401_UNAUTHORIZED
            )

额外建议:

  1. 序列化器LoginSerializer需确保只包含emailpassword字段,并添加格式验证:
from rest_framework import serializers

class LoginSerializer(serializers.Serializer):
    email = serializers.EmailField(required=True)
    password = serializers.CharField(required=True, write_only=True)
  1. 若你的用户模型使用邮箱作为唯一登录凭证,需在settings.py中配置自定义认证后端:
AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
    # 自定义后端示例
    'yourapp.backends.EmailBackend',
]

EmailBackend的实现示例:

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend

User = get_user_model()

class EmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            user = User.objects.get(email=username)
        except User.DoesNotExist:
            return None
        if user.check_password(password):
            return user
  1. 登出功能可以通过删除用户对应的令牌实现:
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.authtoken.models import Token
from rest_framework import status

@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def user_logout(request):
    Token.objects.filter(user=request.user).delete()
    return Response({"detail": "登出成功"}, status=status.HTTP_200_OK)

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

火山引擎 最新活动