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

如何仅在响应成功时启用OTP认证接口的限流?

解决方案:仅在OTP短信发送成功时触发限流

针对你的需求——只有当OTP短信发送成功(响应200/201)时才启用每分钟1条的限流,以下是具体实现方案:

核心结论

  1. 无法在allow_request中获取响应状态码:因为allow_request是DRF在执行视图逻辑之前调用的前置校验方法,此时响应尚未生成,自然无法获取状态码。
  2. 可以手动触发限流:通过自定义限流类,并在短信发送成功后手动记录限流计数,实现精准控制。

具体实现步骤

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 16:15:26