from .slider_captcha import ( generate_slider_captcha as _base_generate, verify_slider_captcha as _base_verify, verify_slider_trajectory as _base_verify_traj, SliderCaptchaError, ) from .rate_limit import check_rate_limit # 频控阈值:单 IP 10 秒内最多 6 次生成(普通用户基本用不到这个量) SLIDER_GENERATE_LIMIT = 6 SLIDER_GENERATE_WINDOW = 10 # 秒 # 验证失败计数:单 IP 5 分钟内累计 10 次失败即拒绝继续验证, # 防止对单 captcha_key 失败重试耗尽缓存/拖慢接口 SLIDER_VERIFY_FAIL_LIMIT = 10 SLIDER_VERIFY_FAIL_WINDOW = 300 # 秒 def _client_ip(request) -> str: fwd = request.META.get('HTTP_X_FORWARDED_FOR') if fwd: return fwd.split(',')[0].strip() return request.META.get('REMOTE_ADDR', '') def generate_slider_captcha(request=None): if request is not None: ip = _client_ip(request) if ip and not check_rate_limit('slider_gen', ip, SLIDER_GENERATE_LIMIT, SLIDER_GENERATE_WINDOW): from utils.response_codes import ResponseCode, create_standardized_error_response from rest_framework import status return create_standardized_error_response( code=ResponseCode.RATE_LIMITED, message='请求过于频繁,请稍后再试', status_code=status.HTTP_429_TOO_MANY_REQUESTS, ) return _base_generate() def verify_slider_captcha(captcha_key: str, x_position: int, request=None) -> bool: ok = _base_verify(captcha_key, x_position) if request is not None and not ok: ip = _client_ip(request) if ip: check_rate_limit('slider_fail', ip, SLIDER_VERIFY_FAIL_LIMIT, SLIDER_VERIFY_FAIL_WINDOW) return ok def verify_slider_trajectory(captcha_key: str, x_position: int, trajectory, request=None) -> bool: ok = _base_verify_traj(captcha_key, x_position, trajectory) if request is not None and not ok: ip = _client_ip(request) if ip: check_rate_limit('slider_fail', ip, SLIDER_VERIFY_FAIL_LIMIT, SLIDER_VERIFY_FAIL_WINDOW) return ok def is_captcha_error(err) -> bool: return isinstance(err, SliderCaptchaError)