【问题标题】:How to wait for the reCAPTCHA v3 token synchronously?如何同步等待 reCAPTCHA v3 令牌?
【发布时间】:2020-05-26 06:17:08
【问题描述】:

我陷入了困境,Javascript 世界很奇怪。

在继续提交表单之前,我需要使用 resolved 制作一个带有 reCAPTCHA v3 令牌的表单。由于某些事件绑定到submit 事件的形式,事件循环必须等到它被解决,否则它会失败:

  • 如果出现错误(例如输入中的无效值),某些表单具有阻止提交的验证事件。
  • 如果用户在页面加载时点击表单的速度过快(由于自动完成),并且仍未检索到令牌,则请求会返回错误。

虽然我试图做的是为submit 事件设置一个侦听器,该事件在令牌被解析之前阻止提交,然后再继续使用其他侦听器。

目前关于 reCAPTCHA 脚本的文档为零,我还没有找到一种可靠的方法来实际有效地解析令牌。 reCAPTCHA 脚本是异步的,所以没有办法等到令牌被解析

// Let's retrieve the form by its id.
let form = document.getElementById('example_form');

let addToken = (form) => {
    grecaptcha.execute('site_key', {
        // some options
    }).then(token => {
        // Include the token as an input inside the form so it's sent.
    });
};

form.addEventListener('submit', () => {
    return addToken(form); // <-- This won't work, since it's async.
});

【问题讨论】:

    标签: javascript asynchronous async-await dom-events recaptcha


    【解决方案1】:

    您可以在加载令牌之前禁用表单提交并改为显示一些加载程序。在then 回调中,您将令牌保存在表单字段中并启用表单提交(并隐藏加载程序)。

    如果你想让它更加无缝,你可以对用户隐藏这个过程:如果用户在加载令牌之前提交表单,然后你禁用按钮(并防止常规提交尝试)并显示一个加载器,并记住提交处于待处理状态,当稍后收到令牌时,您检查提交是否处于待处理状态,如果是,则以编程方式提交表单(包括令牌)。

    这样用户就不会遇到加载器,除非他们实际上过早提交表单。

    【讨论】:

    • 如果我这样做,并且令牌过期,我将不得不间隔刷新每个加载的令牌。这不是最好的解决方案。
    • 对不起,我不明白问题到底是什么......当然不能同步,因为它是网络请求。所以这只是一个问题何时你想加载它。你不能让事件循环等待,这是一个很大的禁忌。
    • 有一个想法是在提交时检索以避免令牌过期,等待检索到继续提交。
    • 好的,对提交尝试做出反应,取消默认操作(实际提交),禁用按钮,显示加载器,加载令牌。加载(异步)后,保存令牌并以编程方式触发实际提交。
    【解决方案2】:

    想通了,没有办法使recaptcha.execute() 同步。换句话说,等待从服务器解析令牌是不可能的。

    相反,您应该通过请求初始令牌,然后以 100 秒的时间间隔设置相同的操作,以确保令牌在到期前被接收,从而漫不经心地敲击 reCAPTCHA 服务器。

    此代码已针对多种形式进行了调整。使用风险自负。

    const site_key = 'HEREYOURSITEKEY';
    
    // This function retrieves the token from reCAPTCHA.
    const retrieveToken = (form) => {
        // Mark the form as unresolved until the retrieval ends.
        form.unresolved = true;
        // Get the token.
        grecaptcha.execute(site_key, {
            action: form.action.substring(action.indexOf('?'), action.length).replace(/[^A-z\/_]/gi, '')
        }).then(token => {
            // Append the token to the form so it's sent along the other data.
            let child = document.createElement('input');
            child.setAttribute('type', 'hidden');
            child.setAttribute('name', '_recaptcha');
            child.setAttribute('value', token);
            form.appendChild(child);
            form.unresolved = false;
        });
    };
    
    // We will loop for each form in the document. You can add a "filter" method if you want.
    Array.from(document.getElementByTagName('form'))
        .forEach(form => {
            // Mark it as unresolved from the beginning.
            form.unresolved = true;
            // Add an event listener that disables submission if the form is unresolved.
            form.addEventListener('submit', event => {
                if (form.unresolved) {
                    event.preventDefault();
                }
            });
            // Resolve the token at startup.
            retrieveToken(form);
            // And retrieve a new token each 100 seconds. Bite me.
            setInterval(() => refreshToken(form), 100 * 1000);
        });
    

    【讨论】:

      【解决方案3】:

      我有同样的问题。这是我的解决方案:

      async function recaptchaCall(){
        var recaptcha_token = '';
        grecaptcha.ready(() => {
          grecaptcha.execute(grecaptchaKey, {action: 'submit'}).then((token) => {
            recaptcha_token = token;
          });
        });
        while(recaptcha_token == ''){
          await new Promise(r => setTimeout(r, 100));
        }
        return recaptcha_token; 
      }
      
      let recaptcha_token = await recaptchaCall();
      

      我认为这是最好的解决方案,因为它不可能正确地作为同步功能。我正在使用 setTimeout 等到收到 Recaptcha 答案。希望对您有所帮助。

      【讨论】:

        【解决方案4】:

        JavaScript 无法等待重新验证请求完成。 我使用时间戳全局变量完成了这项工作,我将其与每次提交时的当前时间戳进行比较。因此,onSubmit 有两种行为:如果需要请求新令牌,则 onSubmit 只处理重新验证令牌(阻止提交)。如果没有必要,那么它会照常进行(验证和其他东西)。

        <script src="https://www.google.com/recaptcha/api.js?render=SITE_KEY"></script>
        
        <script> 
            
                $(function() {
            
                    // last recaptcha token timestamp in ms (to check if the refresh interval of max 2 minutes is reached in between submit attempts)
                    recaptchaLoginLastTokenTimestamp = 0;
            
                    $("#loginform").on('submit', function(e){   
                        
                        nowTimestamp = new Date().getTime();
            
                        // if 100 seconds passed since last recaptcha token => get new token
                        if(nowTimestamp - recaptchaLoginLastTokenTimestamp > 100000){
                            // stop submission
                            e.preventDefault();
            
                            // call the recaptcha service to get a token
                            grecaptcha.ready(function() {
                                grecaptcha.execute('SITE_KEY', {action:'action_login'})
                                        .then(function(token) 
                                {
                                    // add token value to form
                                    document.getElementById('recaptcha_response_token').value = token;
            
                                    // update last token timestamp
                                    recaptchaLoginLastTokenTimestamp = new Date().getTime();
            
                                    // re-submit (with the updated token timestamp, the normal submit process will occur, with validations and other stuff that are bind to submission; no infinite recursive submit calls)
                                    $("#loginform").submit();
                                });    
                            });
                        }  
                                  
                    });
            
                });
            
            </script>
        

        【讨论】:

          猜你喜欢
          • 2019-06-23
          • 1970-01-01
          • 2023-01-27
          • 1970-01-01
          • 2021-04-14
          • 1970-01-01
          • 1970-01-01
          • 2016-08-15
          • 1970-01-01
          相关资源
          最近更新 更多