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

Django REST Framework邮箱验证接口遇CSRF token未设置问题求助

解决DRF VerifyEmailView接口的"CSRF token not set"错误

问题描述

使用Django REST Framework开发API,采用Simple JWT做身份验证,要求用户登录前完成邮箱验证。测试verify-email接口时,出现**"CSRF token not set"**错误,即使给post方法添加了@csrf_exempt装饰器也无法解决。

问题代码

from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework import status
from django.views.decorators.csrf import csrf_exempt
from .models import OneTimePassword
from .serializers import VerfyEmailSerializer

class VerifyEmailView(GenericAPIView):
    serializer_class=VerfyEmailSerializer
    @csrf_exempt
    def post(self,request):
        serializer=self.get_serializer(data=request.data)
        if not serializer.is_valid():
            return Response(
                {'message':'Invalid Input','error':serializer.errors},
                status=status.HTTP_400_BAD_REQUEST
            )
        else:
            otpcode=serializer.validated_data['totp']

            try:
                # Find the latest OTP for any user (since email isn’t provided)
                user_code_obj = OneTimePassword.objects.latest('created_at')
                user = user_code_obj.user

                if user_code_obj.is_expired():
                    user_code_obj.delete()
                    print(user_code_obj)
                    return Response(
                        {'message': 'OTP expired'},
                        status=status.HTTP_400_BAD_REQUEST
                    )

                if user_code_obj.verify_Otp(otpcode):  # Pass the submitted OTP
                    user_code_obj.delete()
                    if not user.is_verified:
                        user.is_verified = True
                        user.save()
                        print(user)
                        return Response(
                            {'message': 'Account email verified successfully'},
                            status=status.HTTP_200_OK
                        )
                    return Response(
                        {'message': 'User already verified'},
                        status=status.HTTP_200_OK
                    )

                return Response(
                    {'message': 'Invalid OTP code'},
                    status=status.HTTP_400_BAD_REQUEST
                )
            except OneTimePassword.DoesNotExist:
                return Response(
                    {'message': 'No valid OTP found'},
                    status=status.HTTP_404_NOT_FOUND
                )

原因分析

@csrf_exempt装饰器直接加在post方法上不生效,因为Django REST Framework的GenericAPIView通过dispatch方法统一处理请求分发,CSRF检查逻辑在dispatch中执行,单独给post方法加装饰器无法绕过这一层检查。

解决方案

方案1:用method_decorator装饰类的dispatch方法

这是DRF视图中豁免CSRF的标准做法:

from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt

@method_decorator(csrf_exempt, name='dispatch')
class VerifyEmailView(GenericAPIView):
    serializer_class=VerfyEmailSerializer
    
    def post(self,request):
        # 原post方法代码不变

方案2:重写dispatch方法并添加装饰器

class VerifyEmailView(GenericAPIView):
    serializer_class=VerfyEmailSerializer
    
    @csrf_exempt
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)
    
    def post(self,request):
        # 原post方法代码不变

额外逻辑优化建议

当前代码存在安全隐患:通过OneTimePassword.objects.latest('created_at')获取最新OTP,会导致任意用户提交的OTP都匹配系统中最新生成的OTP,不管归属用户。正确做法是让前端提交OTP时附带用户邮箱(或ID),关联到对应用户的OTP记录:

# 修改Serializer,添加邮箱字段
class VerifyEmailSerializer(serializers.Serializer):
    totp = serializers.CharField()
    email = serializers.EmailField()

# 在post方法中查询对应用户的OTP
user = User.objects.get(email=serializer.validated_data['email'])
user_code_obj = OneTimePassword.objects.filter(user=user).latest('created_at')

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 20:05:19