From 4f1a1ec97c18df6d4dcd62ed24ebe362f5f9d548 Mon Sep 17 00:00:00 2001 From: yuyx <237899745@qq.com> Date: Fri, 28 Nov 2025 14:06:23 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81=E8=AF=B7=E6=B1=82429=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: - 短时间内多次请求验证码触发限流(429 Too Many Requests) 修复: - 后端:验证码最小请求间隔从3秒改为1秒 - 前端:添加2秒防抖,避免重复请求 - 前端:429错误时保留已有验证码图片 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- backend/server.js | 2 +- frontend/app.js | 58 ++++++++++++++++++++++------------------------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/backend/server.js b/backend/server.js index 2a217fa..07bc224 100644 --- a/backend/server.js +++ b/backend/server.js @@ -755,7 +755,7 @@ const fileListLimiter = new RateLimiter({ }); // 验证码最小请求间隔控制 -const CAPTCHA_MIN_INTERVAL = 3000; // 3秒 +const CAPTCHA_MIN_INTERVAL = 1000; // 1秒 const captchaLastRequest = new TTLCache(15 * 60 * 1000); // 15分钟自动清理 // 验证码防刷中间件 diff --git a/frontend/app.js b/frontend/app.js index 28cefff..1b9d270 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -622,52 +622,48 @@ handleDragLeave(e) { } }, - // 刷新验证码(登录) - async refreshCaptcha() { + // 通用验证码加载函数(带防抖) + async loadCaptcha(targetField) { + // 防抖:2秒内不重复请求 + const now = Date.now(); + if (this._lastCaptchaTime && (now - this._lastCaptchaTime) < 2000) { + console.log('[验证码] 请求过于频繁,跳过'); + return; + } + this._lastCaptchaTime = now; + try { - const response = await axios.get(`${this.apiBase}/api/captcha?t=${Date.now()}`, { + const response = await axios.get(`${this.apiBase}/api/captcha?t=${now}`, { responseType: 'blob' }); - this.captchaUrl = URL.createObjectURL(response.data); + this[targetField] = URL.createObjectURL(response.data); } catch (error) { console.error('获取验证码失败:', error); + // 如果是429错误,不清除已有验证码 + if (error.response?.status !== 429) { + this[targetField] = ''; + } } }, + // 刷新验证码(登录) + refreshCaptcha() { + this.loadCaptcha('captchaUrl'); + }, + // 刷新注册验证码 - async refreshRegisterCaptcha() { - try { - const response = await axios.get(`${this.apiBase}/api/captcha?t=${Date.now()}`, { - responseType: 'blob' - }); - this.registerCaptchaUrl = URL.createObjectURL(response.data); - } catch (error) { - console.error('获取验证码失败:', error); - } + refreshRegisterCaptcha() { + this.loadCaptcha('registerCaptchaUrl'); }, // 刷新忘记密码验证码 - async refreshForgotPasswordCaptcha() { - try { - const response = await axios.get(`${this.apiBase}/api/captcha?t=${Date.now()}`, { - responseType: 'blob' - }); - this.forgotPasswordCaptchaUrl = URL.createObjectURL(response.data); - } catch (error) { - console.error('获取验证码失败:', error); - } + refreshForgotPasswordCaptcha() { + this.loadCaptcha('forgotPasswordCaptchaUrl'); }, // 刷新重发验证邮件验证码 - async refreshResendVerifyCaptcha() { - try { - const response = await axios.get(`${this.apiBase}/api/captcha?t=${Date.now()}`, { - responseType: 'blob' - }); - this.resendVerifyCaptchaUrl = URL.createObjectURL(response.data); - } catch (error) { - console.error('获取验证码失败:', error); - } + refreshResendVerifyCaptcha() { + this.loadCaptcha('resendVerifyCaptchaUrl'); }, async resendVerification() {