如何仅在响应成功时启用OTP认证接口的限流?
解决方案:仅在OTP短信发送成功时触发限流
针对你的需求——只有当OTP短信发送成功(响应200/201)时才启用每分钟1条的限流,以下是具体实现方案:
核心结论
- 无法在
allow_request中获取响应状态码:因为allow_request是DRF在执行视图逻辑之前调用的前置校验方法,此时响应尚未生成,自然无法获取状态码。 - 可以手动触发限流:通过自定义限流类,并在短信发送成功后手动记录限流计数,实现精准控制。
具体实现步骤
1. 自定义OTP专属限流类
重写限流类的get_cache_key方法,基于请求中的手机号(而非IP)生成缓存键,确保同一个手机号的限流规则生效:
from rest_framework.throttling import AnonRateThrottle class OTPRateThrottle(AnonRateThrottle): scope = 'burst' # 复用settings中定义的'1/min'限流规则 def get_cache_key(self, request, view): # 从请求参数中获取手机号(根据你的实际字段名调整) mobile = request.data.get('mobile') if mobile: # 用手机号作为限流标识,确保同一手机号统一限流 return f'throttle_{self.scope}_anon_{mobile}' # 无手机号时 fallback 到IP限流(可选,也可返回None跳过限流) return super().get_cache_key(request, view)
2. 修改视图逻辑,手动控制限流
移除视图上的@throttle_classes装饰器,改为在短信发送成功后手动触发计数:
from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import AllowAny from rest_framework.response import Response from twilio.rest import Client from twilio.base.exceptions import TwilioRestException @api_view(['POST']) @permission_classes([AllowAny]) def login_send_token(request): mobile = request.data.get('mobile') # 1. 校验手机号必填性与格式 if not mobile: return Response({'error': '手机号为必填项'}, status=400) if not is_valid_mobile(mobile): # 需自行实现手机号格式校验函数 return Response({'error': '手机号格式错误'}, status=400) # 2. 初始化限流类,检查当前是否已达限流阈值 throttle = OTPRateThrottle() throttle.request = request throttle.view = login_send_token if not throttle.allow_request(request, login_send_token): return Response({'error': '发送过于频繁,请1分钟后再尝试'}, status=429) # 3. 调用Twilio API发送OTP短信 try: client = Client('你的Twilio账号SID', '你的Twilio认证令牌') message = client.messages.create( body='你的OTP验证码内容', from_='你的Twilio号码', to=mobile ) # 短信发送成功,手动增加限流计数 throttle.incr_history() return Response({'message': 'OTP短信已成功发送'}, status=200) except TwilioRestException as e: return Response({'error': f'短信发送失败:{str(e)}'}, status=500)
3. 保留原有Settings配置
你的REST_FRAMEWORK配置无需修改,'burst': '1/min'的规则会被自定义限流类复用:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework_simplejwt.authentication.JWTAuthentication',), 'DEFAULT_THROTTLE_CLASSES': [ 'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.UserRateThrottle' ], 'DEFAULT_THROTTLE_RATES': { 'burst': '1/min', 'anon': '200/min', 'user': '250/min', }, }
逻辑说明
- 手机号格式错误或缺失时,直接返回400,不会触发任何限流校验。
- 手机号合法时,先检查是否在1分钟内已成功发送过短信:
- 若已达限流阈值,返回429;
- 若未达阈值,尝试发送短信,仅当Twilio返回成功时,才记录限流计数,确保只有成功发送的请求才会占用限流额度。
内容的提问来源于stack exchange,提问作者Hazem Elmahy
相关产品推荐
相关产品推荐

