Javascript 检测Google recaptcha的质询窗口何时关闭
我用的是谷歌隐形的recaptcha。是否有办法检测挑战窗口何时关闭?我所说的挑战窗口是指你必须挑选一些图像进行验证的窗口 当前,我在呈现recaptcha挑战的按钮上放置了一个微调器,只要单击该按钮。用户无法通过另一个质询窗口得到提示 我正在以编程方式调用render函数: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
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的两个特性,因此如果代码不起作用,请首先查看:
- 窗口
src:包含“google.com/recaptcha/api2/bframe”iframe
- CSS
属性:关闭窗口时更改为0opacity
要在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']
});
});