Javascript 检测Google recaptcha的质询窗口何时关闭

Javascript 检测Google recaptcha的质询窗口何时关闭,javascript,callback,recaptcha,invisible-recaptcha,Javascript,Callback,Recaptcha,Invisible Recaptcha,我用的是谷歌隐形的recaptcha。是否有办法检测挑战窗口何时关闭?我所说的挑战窗口是指你必须挑选一些图像进行验证的窗口 当前,我在呈现recaptcha挑战的按钮上放置了一个微调器,只要单击该按钮。用户无法通过另一个质询窗口得到提示 我正在以编程方式调用render函数: grecaptcha.render(htmlElement, { callback: this.verified, expiredCallback: this.resetRecaptcha, sitekey: this.s

我用的是谷歌隐形的recaptcha。是否有办法检测挑战窗口何时关闭?我所说的挑战窗口是指你必须挑选一些图像进行验证的窗口

当前,我在呈现recaptcha挑战的按钮上放置了一个微调器,只要单击该按钮。用户无法通过另一个质询窗口得到提示

我正在以编程方式调用render函数:

grecaptcha.render(htmlElement, { callback: this.verified, expiredCallback: this.resetRecaptcha, sitekey: this.siteKey, theme: "light", size: "invisible" });
我有两个回调函数连接到verified和resetRecaptcha函数,它们看起来像:

function resetRecaptcha() {
        grecaptcha.reset();
    }

function verified(recaptchaResponse)
{
/*
which calls the server to validate
*/
}

我本以为grecaptcha.render会有另一个回调,当质询屏幕关闭时,用户不会通过选择图像来验证自己。

如您所述,API不支持此功能

但是,您可以自己添加此功能。您可以谨慎使用以下代码,Google可能会更改其reCaptcha,从而破坏此自定义代码。该解决方案依赖于reCaptcha的两个特性,因此如果代码不起作用,请首先查看:

  • 窗口
    iframe
    src:包含“google.com/recaptcha/api2/bframe”
  • CSS
    opacity
    属性:关闭窗口时更改为0


要在IE中工作,此解决方案需要.include()和Array.from()的多边形填充,如下所示:

更新后的代码:

函数initListener(){

我的解决方案:

let removeRecaptchaOverlayEventListener = null
const reassignGRecatchaExecute = () => {
  if (!window.grecaptcha || !window.grecaptcha.execute) {
    return
  }
  /* save original grecaptcha.execute */
  const originalExecute = window.grecaptcha.execute
  window.grecaptcha.execute = (...params) => {
    try {
      /* find challenge iframe */
      const recaptchaIframe = [...document.body.getElementsByTagName('iframe')].find(el => el.src.match('https://www.google.com/recaptcha/api2/bframe'))
      const recaptchaOverlay = recaptchaIframe.parentElement.parentElement.firstElementChild
      /* detect when the recaptcha challenge window is closed and reset captcha */
      !removeRecaptchaOverlayEventListener && recaptchaOverlay.addEventListener('click', window.grecaptcha.reset)
      /* save remove event listener for click event */
      removeRecaptchaOverlayEventListener = () => recaptchaOverlay.removeEventListener('click', window.grecaptcha.reset)
    } catch (error) {
      console.error(error)
    } finally {
      originalExecute(...params)
    }
  }
}
在运行
window.grecaptcha.render()之后和
window.grecaptcha.execute()之前调用此函数


不要忘了删除事件监听器:
removeCaptChaoverLayeventListener()

正如我在@arcs提交的答案评论中提到的,这是一个很好的解决方案,它可以工作,但当用户成功完成挑战时,它也会触发
onClose()
。我的解决方案是更改
onClose()
功能如下:

// now do something with this information
function onClose() {
    if(!grecaptcha.getResponse()) {
        console.log('recaptcha window has been closed')
    }
}

这样,只有在质询已关闭且用户尚未完成时,它才会执行所需的代码,因此无法使用
grecaptcha.getResponse()

返回响应。对于所有不太了解其工作原理的人,下面是另一个示例,其中的解释可能对您有用:

因此,我们面临两个挑战

1)检测何时显示质询并获取质询的叠加div

function detectWhenReCaptchaChallengeIsShown() {
    return new Promise(function(resolve) {
        const targetElement = document.body;

        const observerConfig = {
            childList: true,
            attributes: false,
            attributeOldValue: false,
            characterData: false,
            characterDataOldValue: false,
            subtree: false
        };

        function DOMChangeCallbackFunction(mutationRecords) {
            mutationRecords.forEach((mutationRecord) => {
                if (mutationRecord.addedNodes.length) {
                    var reCaptchaParentContainer = mutationRecord.addedNodes[0];
                    var reCaptchaIframe = reCaptchaParentContainer.querySelectorAll('iframe[title*="recaptcha"]');

                    if (reCaptchaIframe.length) {
                        var reCaptchaChallengeOverlayDiv = reCaptchaParentContainer.firstChild;
                        if (reCaptchaChallengeOverlayDiv.length) {
                            reCaptchaObserver.disconnect();
                            resolve(reCaptchaChallengeOverlayDiv);
                        }
                    }
                }
            });
        }

        const reCaptchaObserver = new MutationObserver(DOMChangeCallbackFunction);
        reCaptchaObserver.observe(targetElement, observerConfig);
    });
}
首先,我们创建了一个目标元素,用于观察Google iframe的外观。我们将document.body作为iframe的目标元素附加到它:

const targetElement = document.body;
然后,我们为MutationObserver创建了一个配置对象。在这里,我们可以指定在DOM更改中跟踪的确切内容。请注意,默认情况下,所有值都是“false”,因此我们只能保留“childList”-这意味着在本例中,我们将只观察目标元素document.body的子节点更改:

const observerConfig = {
    childList: true,
    attributes: false,
    attributeOldValue: false,
    characterData: false,
    characterDataOldValue: false,
    subtree: false
};
然后,我们创建了一个函数,当观察者检测到我们在config object中指定的特定类型的DOM更改时,该函数将被调用。第一个参数表示一个对象数组。我们获取了overlay div并返回了Promise

function DOMChangeCallbackFunction(mutationRecords) {
    mutationRecords.forEach((mutationRecord) => {
        if (mutationRecord.addedNodes.length) { //check only when notes were added to DOM
            var reCaptchaParentContainer = mutationRecord.addedNodes[0];
            var reCaptchaIframe = reCaptchaParentContainer.querySelectorAll('iframe[title*="recaptcha"]');

            if (reCaptchaIframe.length) { // Google reCaptcha iframe was loaded
                var reCaptchaChallengeOverlayDiv = reCaptchaParentContainer.firstChild;
                if (reCaptchaChallengeOverlayDiv.length) {
                    reCaptchaObserver.disconnect(); // We don't want to observe more DOM changes for better performance
                    resolve(reCaptchaChallengeOverlayDiv); // Returning the overlay div to detect close events
                }
            }
        }
    });
}
最后,我们实例化了一个观察者本身,并开始观察DOM的变化:

const reCaptchaObserver = new MutationObserver(DOMChangeCallbackFunction);
reCaptchaObserver.observe(targetElement, observerConfig);
2)第二个挑战是那篇文章的主要问题-我们如何检测到挑战已经结束?好吧,我们再次需要MutationObserver的帮助。

detectReCaptchaChallengeAppearance().then(function (reCaptchaChallengeOverlayDiv) {
    var reCaptchaChallengeClosureObserver = new MutationObserver(function () {
        if ((reCaptchaChallengeOverlayDiv.style.visibility === 'hidden') && !grecaptcha.getResponse()) {
            // TADA!! Do something here as the challenge was either closed by hitting outside of an overlay div OR by pressing ESC key
            reCaptchaChallengeClosureObserver.disconnect();
        }
    });
    reCaptchaChallengeClosureObserver.observe(reCaptchaChallengeOverlayDiv, {
        attributes: true,
        attributeFilter: ['style']
    });
});
因此,我们所做的是,我们得到了Google reCaptcha挑战覆盖div,并在步骤1中创建了承诺,然后我们订阅了覆盖div上的“样式”更改。这是因为当挑战结束时,Google会将其淡出。 重要的是要注意,当一个人成功解决验证码时,可见性也将隐藏。这就是为什么我们添加了!grecaptcha.getResponse()检查。除非问题得到解决,否则它不会返回任何结果。
这差不多就是它-我希望这有帮助:)

请向我们展示你已经拥有的代码我认为这里不需要代码@arcs。我有同样的问题-如何检测challange小部件刚刚关闭,而没有其他操作?@knaos你是对的。我在下面发布了解决方案…2个月后这在除IE-is之外的所有方面都非常有效这只是一个重构ES6的案例?@Doughballs是的,如果你修复了ES6,它应该可以在IE11上工作。但是,它在IE10和更低版本上不起作用。这很好,但是一旦成功完成recaptcha挑战时,onClose也会触发。效果很好,当然你必须用实际的选择器替换recaptchaButton。下面的查询选择器是pr如果以英语以外的语言加载reCAPTCHA,则会失败:
reCaptchaParentContainer.querySelectorAll('iframe[title=“reCAPTCHA challenge”]);
。例如,在pt BR中,标题是“desafio reCAPTCHA”。谢谢!我已将其更改为更加防弹的“iframe[title*=”reCAPTCHA”]
const reCaptchaObserver = new MutationObserver(DOMChangeCallbackFunction);
reCaptchaObserver.observe(targetElement, observerConfig);
detectReCaptchaChallengeAppearance().then(function (reCaptchaChallengeOverlayDiv) {
    var reCaptchaChallengeClosureObserver = new MutationObserver(function () {
        if ((reCaptchaChallengeOverlayDiv.style.visibility === 'hidden') && !grecaptcha.getResponse()) {
            // TADA!! Do something here as the challenge was either closed by hitting outside of an overlay div OR by pressing ESC key
            reCaptchaChallengeClosureObserver.disconnect();
        }
    });
    reCaptchaChallengeClosureObserver.observe(reCaptchaChallengeOverlayDiv, {
        attributes: true,
        attributeFilter: ['style']
    });
});