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

React应用中reCAPTCHA v.3不同Action二次调用返回相同Token问题

React中reCAPTCHA Enterprise v3重复调用返回相同Token的问题解决

问题分析

你遇到的第二次调用window.grecaptcha.enterprise.execute返回相同Token的问题,大概率是并发请求竞态或状态更新异步性导致的:

  • 当isSubmit快速连续触发两次时,第一次的execute请求还未完成,第二次请求就已发起,可能导致旧的请求结果覆盖新的;
  • React的setState是异步的,你在调用execute前先setToken(''),但这个状态更新可能还未完成,就开始了新的请求流程,最终导致Token重复。

解决方案

1. 添加请求状态锁,避免并发调用

使用useRef创建一个标志位,跟踪当前是否有正在进行的reCAPTCHA请求,防止重复触发execute:

import { useState, useEffect, useRef } from 'react';

export const useReCaptcha = ({ reCaptchaKey, action, isSubmit }: UseReCaptchaProps): string => {
  const [token, setToken] = useState<string>('');
  const [scriptLoaded, setScriptLoaded] = useState<boolean>(false);
  // 添加请求锁,防止并发请求
  const isRequesting = useRef(false);

  useEffect(() => {
    const script = document.createElement('script');
    script.src = `https://www.google.com/recaptcha/enterprise.js?render=${reCaptchaKey}`;
    script.onload = () => {
      setScriptLoaded(true);
    };
    script.onerror = () => {
      console.error('Failed to load reCAPTCHA script');
      setToken(RECAPTCHA_ERROR_MESSAGE);
    };
    document.head.appendChild(script);

    return () => {
      document.head.removeChild(script);
    };
  }, []);

  useEffect(() => {
    // 仅在满足条件时执行请求
    if (isSubmit && scriptLoaded && !isRequesting.current) {
      isRequesting.current = true;
      setToken('');
      window.grecaptcha.enterprise.ready(async () => {
        try {
          const reCaptchaToken = await window.grecaptcha.enterprise.execute(reCaptchaKey, { action });
          setToken(reCaptchaToken);
        } catch (error) {
          console.error(error);
          setToken(RECAPTCHA_ERROR_MESSAGE);
        } finally {
          // 请求结束后释放锁
          isRequesting.current = false;
        }
      });
    }
  }, [isSubmit, scriptLoaded, reCaptchaKey, action]);

  return token;
};

2. 确保isSubmit正确重置

在使用这个Hook的父组件中,务必在Token返回后将isSubmit重置为false,避免重复触发请求:

const [isSubmit, setIsSubmit] = useState(false);
const token = useReCaptcha({ reCaptchaKey: '你的密钥', action: '自定义动作', isSubmit });

// 当Token获取完成后重置isSubmit
useEffect(() => {
  if (token && token !== RECAPTCHA_ERROR_MESSAGE) {
    // 这里处理Token(比如提交到后端)
    console.log('获取到Token:', token);
    // 重置isSubmit,防止重复触发
    setIsSubmit(false);
  }
}, [token]);

// 触发提交的逻辑
const handleSubmit = () => {
  setIsSubmit(true);
};

3. 验证Action参数的唯一性

虽然你确认Action每次不同,但可以在调用execute前打印Action值,确保没有意外的重复或状态未更新的情况:

// 在execute前添加日志,验证Action是否正确
console.log('当前请求的Action:', action);
const reCaptchaToken = await window.grecaptcha.enterprise.execute(reCaptchaKey, { action });

额外说明

reCAPTCHA Enterprise v3本身会对短时间内的请求有缓存机制,但针对不同Action的请求理论上不会返回相同Token。如果上述方案无效,可以尝试:

  • 检查是否有全局的reCAPTCHA脚本重复加载的情况;
  • 确保你的reCAPTCHA密钥配置正确,没有权限或限制问题。

内容的提问来源于stack exchange,提问作者Gleb_Sh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 16:34:53