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
相关产品推荐
相关产品推荐

