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
相关产品推荐
相关产品推荐

