Recaptcha V3持续获0.1/0.3低分致用户无法登录,求排查
ReCAPTCHA v3 低分及登录失败问题排查
代码与配置中的核心问题
1. 表单提交逻辑缺失关键步骤
你的jQuery表单提交代码里,用e.preventDefault()阻止了默认提交,但获取到Token后没有触发表单提交动作。这会导致实际提交的可能是旧Token、甚至空Token,直接拉低验证分数,甚至导致验证失败。
修复代码:
$(document).ready(function(){ $(".recaptcha_form").on("submit", function(e){ e.preventDefault(); const $currentForm = $(this); grecaptcha.ready(function() { // 根据当前表单的操作设置对应action,比如登录用'login' grecaptcha.execute('<?php echo RECAPTCHA_SITE_KEY; ?>', {action: 'login'}).then(function(token) { // 仅给当前表单的Token字段赋值,避免跨表单干扰 $currentForm.find('[name="token"]').val(token); $currentForm.submit(); // 获取Token后立即提交表单 }); }); }); });
2. Action参数过度单一
所有验证都用action: 'homepage',但ReCAPTCHA v3的action需要和具体操作对应(登录用login、注册用register、密码修改用password_change)。单一action会让Google无法区分操作风险,直接导致分数计算不准确。
同时后端要校验返回的action字段,防止Token被跨操作复用:
// 验证成功后添加action校验 if($captchaResponse['action'] !== 'login') { die(json_encode(['status' => 'error', 'message' => '验证码操作不匹配'])); }
3. Token刷新逻辑冗余混乱
- 重复定义
loadCaptcha和loadCaptcha2,功能完全重叠,没必要拆分 - 一次性给所有页面的Token字段赋值,会导致不同表单的Token互相覆盖
- 注释与代码逻辑不符(注释写3秒刷新,实际是90秒后每105秒刷新)
优化后的刷新逻辑(仅针对需要预加载Token的表单,比如头部登录框):
function refreshCaptcha(targetSelector, action) { grecaptcha.ready(function() { grecaptcha.execute('<?php echo RECAPTCHA_SITE_KEY; ?>', {action: action}).then(function(token) { $(targetSelector).val(token); }); }); } $(document).ready(function() { const loginHeaderSelector = '#tokenLoginHeader'; // 初始化头部登录框的Token refreshCaptcha(loginHeaderSelector, 'login'); // Token有效期120秒,在过期前15秒刷新 setTimeout(function() { setInterval(() => refreshCaptcha(loginHeaderSelector, 'login'), 105 * 1000); }, 90 * 1000); });
4. 后端验证缺少关键参数与错误处理
- 未传入用户
remoteip:Google的验证接口需要用户IP来评估风险,缺少该参数会导致分数计算偏差 - 未处理curl请求失败情况:如果请求超时或出错,会直接导致
json_decode返回null,引发后续逻辑错误 - 未校验
success字段:必须先确认验证请求本身成功,再去判断分数是否合格
修复后的后端代码:
$response = $_POST['token'] ?? ''; if(empty($response)) { die(json_encode(['status' => 'error', 'message' => '验证码无效'])); } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://www.recaptcha.net/recaptcha/api/siteverify'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); // 加入remoteip参数 $body = http_build_query([ 'secret' => 'SECRET_KEY', 'response' => $response, 'remoteip' => $_SERVER['REMOTE_ADDR'] ?? '' ]); curl_setopt($ch, CURLOPT_POSTFIELDS, $body); // 设置超时时间,避免请求挂起 curl_setopt($ch, CURLOPT_TIMEOUT, 10); $apiResponse = curl_exec($ch); // 处理curl请求失败 if(curl_errno($ch)) { curl_close($ch); die(json_encode(['status' => 'error', 'message' => '验证码验证失败'])); } curl_close($ch); $captchaResponse = json_decode($apiResponse, true); // 先校验验证是否成功 if(!$captchaResponse || !$captchaResponse['success']) { die(json_encode(['status' => 'error', 'message' => '验证码无效'])); } // 再处理分数判断 $score = $captchaResponse['score'] ?? 0; if($score < 0.1) { die(json_encode(['status' => 'error', 'message' => '验证未通过,请稍后重试'])); }
其他配置排查点
- 域名验证:检查ReCAPTCHA控制台的域名配置,确保网站域名(包括移动端域名、子域名)已正确添加
- 测试环境干扰:本地测试、代理IP会被识别为高风险环境,分数偏低,建议在生产环境或真实用户网络下测试
- 用户环境问题:用户浏览器禁用JS、安装广告拦截插件、频繁切换IP等行为会导致分数极低,可考虑降级为ReCAPTCHA v2复选框模式作为备选方案
内容的提问来源于stack exchange,提问作者Giovanni Paez
相关产品推荐
相关产品推荐

