Google reCAPTCHA v3点击第二个按钮出现timeout-or-duplicate错误原因
timeout-or-duplicate的问题 这个问题我之前排查过不少次,timeout-or-duplicate这个错误码本质上指向两种情况:要么请求到Google验证服务器超时,要么你提交的token已经被验证过一次了。结合你的代码细节,主要有几个高概率的原因和对应的解决办法:
1. 同一个token被重复提交验证
这是最常见的触发这个错误的原因。reCAPTCHA v3的token是一次性有效的,一旦提交到Google的siteverify接口验证过,再次用同一个token验证就会直接返回timeout-or-duplicate。
看你的代码逻辑:在verify函数里已经调用了VerifyCaptchaV3验证token,如果你的Buy Web服务方法内部,又再次调用了VerifyCaptchaV3来验证同一个token,那第二次验证就会触发这个错误——而Contact Us的方法里可能没有重复验证,所以正常工作。
解决办法:
确保每个token只被提交到Google验证一次:
- 方案一:在
verify函数验证通过后,直接执行对应的业务逻辑(调用ContactUs/Buy方法),不在业务方法里重复验证captcha; - 方案二:把
verify里的验证结果(比如success状态)传递给业务方法,业务方法不再重复提交token到Google验证。
2. GET请求的URL编码问题
你的服务器端代码用WebClient.DownloadString以GET方式调用Google的siteverify接口,而reCAPTCHA的token是包含特殊字符的长字符串,直接拼到URL里会出现编码错误(比如+会被解析为空格,部分字符因URL长度限制被截断),这会让Google无法正确识别token,进而返回类似timeout-or-duplicate的错误。
解决办法:
改用POST请求调用siteverify接口,并且用表单数据提交参数(这也是Google推荐的方式)。修改后的C#代码示例:
public string VerifyCaptchaV3(string response) { var postData = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("secret", "your_actual_secret_key"), new KeyValuePair<string, string>("response", response) }; var content = new FormUrlEncodedContent(postData); using (var client = new HttpClient()) { var result = client.PostAsync("https://www.google.com/recaptcha/api/siteverify", content).Result; return result.Content.ReadAsStringAsync().Result; } }
3. reCAPTCHA execute调用缺少action参数
你的前端grecaptcha.execute调用没有传入action参数,而reCAPTCHA v3要求每个操作对应一个唯一的action(比如contactus、buy)。虽然这不一定直接触发timeout-or-duplicate,但不符合Google的规范,可能会影响验证的准确性,甚至在某些边缘场景下触发异常。
另外注意:你代码里grecaptcha.execute('clientkeyValue')的key和加载api时render=keyValue的key不一致,这可能是笔误,要改成同一个站点key。
解决办法:
修改execute调用,传入对应的action参数:
grecaptcha.execute('your_actual_site_key', {action: action})
4. 快速重复点击导致token复用
如果用户快速点击Buy按钮两次,可能第二次请求还没获取到新的token,就复用了第一次的token,导致重复验证。
解决办法:
在按钮点击后暂时禁用按钮,直到ajax请求完成,避免重复点击:
function verify(action) { // 获取当前点击的按钮并禁用 const targetBtn = action === "contactus" ? $("#submit") : $("#submit1"); targetBtn.prop("disabled", true); grecaptcha.execute('your_actual_site_key', {action: action}) .then(function (token) { $.ajax({ type: "POST", url: "SomeWebService.asmx/VerifyCaptchaV3", data: JSON.stringify({ response: token }), success: function (r) { const result = JSON.parse(r); if (result.success === true) { if (action === "contactus") { // 调用ContactUs业务逻辑 } else if (action === "buy") { // 调用Buy业务逻辑 } } else { // 处理验证失败逻辑 } }, error: function() { // 处理请求错误 }, complete: function() { // 请求完成后恢复按钮可用状态 targetBtn.prop("disabled", false); }, contentType: "application/json; charset=utf-8", dataType: "json" }); }); }
总结一下,优先排查重复验证token和GET请求编码这两个问题,大概率能解决你的问题。
内容的提问来源于stack exchange,提问作者Sunil

