不可见的谷歌Recaptcha和ajax表单

时间:2023-01-17 13:15:36

I have an ajax form:

我有一个ajax表单:

  <form id="my_form">
    <input type="text" id="field1" />
    <input type="submit" value="submit" />
  </form>

And js code:

和js代码:

document.getElementById("my_form").onsubmit = function(e) {
  e.preventDefault();

  var xhr = new XMLHttpRequest();
  //.............. send request to a server

In the documentation it assumes that a form is a normal form, not ajax. How exactly should I integrate invisible reCaptcha to my ajax forms? For example:

在文档中,它假定表单是普通的表单,而不是ajax。如何将不可见的reCaptcha集成到ajax表单中?例如:

  <form id="my_form">
    <input type="text" id="field1" />
    <div class="g-recaptcha" data-sitekey="12345" data-callback="????></div>
    <input type="submit" value="submit" />
  </form>

And, in particular, what should I specify for "data-callback" handler? Again, in the documentation it data-callback submits a form, but a normal form, whereas mine is ajax. Do I need "data-callback" at all? Shouldn't I instead call recaptcha inside my handler? How?

特别是,我应该为“数据回调”处理程序指定什么?同样,在文档中,它的数据回调提交了一个表单,但是是一个普通的表单,而我的是ajax。我是否需要“数据回调”?我不应该在我的处理器中调用recaptcha吗?如何?

There're "render", "getResponse" and "execute". Which one should I use? It's not clear from the documentation.

有渲染,getResponse和execute。我应该用哪个?从文件上看不清楚。

2 个解决方案

#1


21  

I agree that "invisible" recaptcha documentation is not comprehensive enough. I had to spend some time digging thru code example and documentations of "visible" recaptcha before understanding how to work with this.

我同意“看不见的”再验证文件不够全面。在理解如何处理这个问题之前,我必须花一些时间来挖掘“可见”的代码示例和文档。

Let talk about the recaptcha API first:

让我们先讨论一下recaptcha API:

grecaptcha.render(htmlEl, options, inherit) is JS API method of rendering the captcha HTML on the page. By default recaptcha script will try to find any element with class="g-recaptcha and try to render immediately, but this behavior can be overridden by appending ?render=explicit query param to recaptcha script src url. You also may want to render the recaptcha html on demand using this api when your recaptcha .g-recaptcha element gets attached to DOM at a point later than when script was loaded. This api returns an ID value that can be passed to other api methods, but if not passed, those api's lookup and reference first repcaptcha on page.

grecaptcha。呈现(htmlEl,选项,继承)是在页面上呈现captcha HTML的JS API方法。默认情况下,recaptcha脚本将尝试查找具有class="g-recaptcha的任何元素并立即呈现,但是可以通过appending ?render=显式查询param来覆盖这个脚本src url。您还可能希望在您的.g . recaptcha元素在加载脚本之后被附加到DOM时使用该api根据需要呈现recaptcha html。这个api返回一个ID值,该值可以传递给其他api方法,但是如果没有传递,这些api的查找和引用将首先在页面上进行repcaptcha。

grecaptcha.getResponse(optional_id) returns the token. If token is empty string, it means user has not been validated yet i.e. user hasn't completed the captcha challenge.

grecaptcha.getResponse(optional_id)返回的令牌。如果令牌为空字符串,则表示用户尚未验证,即用户尚未完成验证码挑战。

grecaptcha.execute(optional_id) api triggers the recaptcha challenge on-demand programmatically. This api is only applicable to "invisible" recaptcha. Visible recaptcha challenges are triggered when user clicks the recaptcha module.

执行(optional_id) api以编程方式触发recaptcha挑战。此api仅适用于“不可见”的recaptcha。当用户单击recaptcha模块时将触发可见的recaptcha挑战。

grecaptcha.reset(optional_id) will reset a challenge i.e. it must be done each time server fails to validate the token with recaptcha api server (because tokens are one time use), but depending on your implementation, you may decide to reset any time.

reset .reset(optional_id)将重置一个挑战,即每次服务器无法验证该令牌,并重新验证该令牌(因为令牌是一次性使用的),但是根据您的实现,您可能决定随时重新设置。

Now, lets talk about data-callback:

现在,我们来谈谈数据回调:

data-callback is an attribute where you can pass a name of global namespaced function, i.e. some function which is accessible as window['nameOfFunction']. This callback will get called each time user is successfully validated with a token value that you will eventually be passing to server. This is same token that is returned by grecaptcha.getResponse() so technically you do not need this function at all. But it can serve as callback to let you know user has passed verification in case you need to update UI or something.

数据回调是一个属性,您可以在其中传递全局名称空间函数的名称,即一些可以作为窗口访问的函数['nameOfFunction']。每次成功地使用令牌值对用户进行验证时,都会调用这个回调,您最终将把令牌值传递给服务器。这与grecaptcha.getResponse()返回的令牌相同,因此从技术上讲,您根本不需要这个函数。但它可以作为回调,让您知道用户已通过验证,以防您需要更新UI或其他什么。

If for some reason you do not want this callback to be accessible from window namespace, you can pass this method in options object with callback key to grecaptcha.render(). NOTE: options.callback can take a string value which is equivalent to passing data-callback attribute in HTML, i.e. is must be a function in window namespace. But options.callback can take a "function" value as well.

如果出于某种原因不希望从窗口名称空间访问这个回调,那么可以将这个方法传递到options对象中,并将回调键传递给grecaptcha.render()。注意:选择。回调可以取一个字符串值,它等价于在HTML中传递数据回调属性,也就是说,它必须是窗口名称空间中的函数。但选项。回调也可以取“函数”值。


Now some sample code:

HTML

HTML

<script src="https://www.google.com/recaptcha/api.js?render=explicit&onload=onScriptLoad" async defer></script>

JS

JS

window.onScriptLoad = function () {
    // this callback will be called by recaptcah/api.js once its loaded. If we used
   // render=explicit as param in script src, then we can explicitly render reCaptcha at this point

    // element to "render" invisible captcha in
    var htmlEl = document.querySelector('.g-recaptcha');

    // option to captcha
    var captchaOptions = {
      sitekey: '6Lck',
      size: 'invisible',
      // tell reCaptcha which callback to notify when user is successfully verified.
      // if this value is string, then it must be name of function accessible via window['nameOfFunc'], 
      // and passing string is equivalent to specifying data-callback='nameOfFunc', but it can be
      // reference to an actual function
      callback: window.onUserVerified
  };

    // Only for "invisible" type. if true, will read value from html-element's data-* attribute if its not passed via captchaOptions
    var inheritFromDataAttr = true;

    // now render
    recaptchaId = window.grecaptcha.render(htmlEl, captchaOptions, inheritFromDataAttr);
};

// this is assigned from "data-callback" or render()'s "options.callback"
window.onUserVerified = function (token) {
    alert('User Is verified');
    console.log('token=', token);
};


// click handler for form's submit button
function onSubmitBtnClick () {      
  var token =   window.grecaptcha.getResponse(recaptchaId);

  // if no token, mean user is not validated yet
  if (!token) {
     // trigger validation
     window.grecaptcha.execute(recaptchaId);
     return;
  }

  var xhrData = {
    'g-recaptcha-response': token
    // more ajax body/data here
  };

  // proceed with appending more ajax call data to xhrData and then rest of ajax call process
  // var xhr = new XMLHttpRequest();
  // ... ... .... ... ... 
}

#2


0  

You can easily validate google recaptcha's using jquery

您可以使用jquery轻松验证谷歌recaptcha

<?php 
$remoteip = $_SERVER['REMOTE_ADDR'];
?>
<script type="text/javascript">
    function reloadRecaptcha() {
        var publicKey = "your_public_key";
        var div = "recap";
        Recaptcha.create(publicKey,div,{theme: "white"});
        return false;
    }
    function validate() {

        var challenge = Recaptcha.get_challenge();
        var response = Recaptcha.get_response();
        var remoteip = "<?php echo $remoteip; ?>";
        $.ajax({
          type: "POST",
          url: "validateRecaptcha.php",
          async: false,
          data: {
            remoteip: remoteip,
            challenge: challenge,
            response: response
          },
          success: function(resp) {
                if(resp == "true") {
                    document.getElementById("message").innerHTML = "Perfect!";
                }
                else {
                    document.getElementById("message").innerHTML = "Incorrect Recaptcha! Please try again!";
                    reloadRecaptcha();
                }
          }
        });
        return false;
    }

#1


21  

I agree that "invisible" recaptcha documentation is not comprehensive enough. I had to spend some time digging thru code example and documentations of "visible" recaptcha before understanding how to work with this.

我同意“看不见的”再验证文件不够全面。在理解如何处理这个问题之前,我必须花一些时间来挖掘“可见”的代码示例和文档。

Let talk about the recaptcha API first:

让我们先讨论一下recaptcha API:

grecaptcha.render(htmlEl, options, inherit) is JS API method of rendering the captcha HTML on the page. By default recaptcha script will try to find any element with class="g-recaptcha and try to render immediately, but this behavior can be overridden by appending ?render=explicit query param to recaptcha script src url. You also may want to render the recaptcha html on demand using this api when your recaptcha .g-recaptcha element gets attached to DOM at a point later than when script was loaded. This api returns an ID value that can be passed to other api methods, but if not passed, those api's lookup and reference first repcaptcha on page.

grecaptcha。呈现(htmlEl,选项,继承)是在页面上呈现captcha HTML的JS API方法。默认情况下,recaptcha脚本将尝试查找具有class="g-recaptcha的任何元素并立即呈现,但是可以通过appending ?render=显式查询param来覆盖这个脚本src url。您还可能希望在您的.g . recaptcha元素在加载脚本之后被附加到DOM时使用该api根据需要呈现recaptcha html。这个api返回一个ID值,该值可以传递给其他api方法,但是如果没有传递,这些api的查找和引用将首先在页面上进行repcaptcha。

grecaptcha.getResponse(optional_id) returns the token. If token is empty string, it means user has not been validated yet i.e. user hasn't completed the captcha challenge.

grecaptcha.getResponse(optional_id)返回的令牌。如果令牌为空字符串,则表示用户尚未验证,即用户尚未完成验证码挑战。

grecaptcha.execute(optional_id) api triggers the recaptcha challenge on-demand programmatically. This api is only applicable to "invisible" recaptcha. Visible recaptcha challenges are triggered when user clicks the recaptcha module.

执行(optional_id) api以编程方式触发recaptcha挑战。此api仅适用于“不可见”的recaptcha。当用户单击recaptcha模块时将触发可见的recaptcha挑战。

grecaptcha.reset(optional_id) will reset a challenge i.e. it must be done each time server fails to validate the token with recaptcha api server (because tokens are one time use), but depending on your implementation, you may decide to reset any time.

reset .reset(optional_id)将重置一个挑战,即每次服务器无法验证该令牌,并重新验证该令牌(因为令牌是一次性使用的),但是根据您的实现,您可能决定随时重新设置。

Now, lets talk about data-callback:

现在,我们来谈谈数据回调:

data-callback is an attribute where you can pass a name of global namespaced function, i.e. some function which is accessible as window['nameOfFunction']. This callback will get called each time user is successfully validated with a token value that you will eventually be passing to server. This is same token that is returned by grecaptcha.getResponse() so technically you do not need this function at all. But it can serve as callback to let you know user has passed verification in case you need to update UI or something.

数据回调是一个属性,您可以在其中传递全局名称空间函数的名称,即一些可以作为窗口访问的函数['nameOfFunction']。每次成功地使用令牌值对用户进行验证时,都会调用这个回调,您最终将把令牌值传递给服务器。这与grecaptcha.getResponse()返回的令牌相同,因此从技术上讲,您根本不需要这个函数。但它可以作为回调,让您知道用户已通过验证,以防您需要更新UI或其他什么。

If for some reason you do not want this callback to be accessible from window namespace, you can pass this method in options object with callback key to grecaptcha.render(). NOTE: options.callback can take a string value which is equivalent to passing data-callback attribute in HTML, i.e. is must be a function in window namespace. But options.callback can take a "function" value as well.

如果出于某种原因不希望从窗口名称空间访问这个回调,那么可以将这个方法传递到options对象中,并将回调键传递给grecaptcha.render()。注意:选择。回调可以取一个字符串值,它等价于在HTML中传递数据回调属性,也就是说,它必须是窗口名称空间中的函数。但选项。回调也可以取“函数”值。


Now some sample code:

HTML

HTML

<script src="https://www.google.com/recaptcha/api.js?render=explicit&onload=onScriptLoad" async defer></script>

JS

JS

window.onScriptLoad = function () {
    // this callback will be called by recaptcah/api.js once its loaded. If we used
   // render=explicit as param in script src, then we can explicitly render reCaptcha at this point

    // element to "render" invisible captcha in
    var htmlEl = document.querySelector('.g-recaptcha');

    // option to captcha
    var captchaOptions = {
      sitekey: '6Lck',
      size: 'invisible',
      // tell reCaptcha which callback to notify when user is successfully verified.
      // if this value is string, then it must be name of function accessible via window['nameOfFunc'], 
      // and passing string is equivalent to specifying data-callback='nameOfFunc', but it can be
      // reference to an actual function
      callback: window.onUserVerified
  };

    // Only for "invisible" type. if true, will read value from html-element's data-* attribute if its not passed via captchaOptions
    var inheritFromDataAttr = true;

    // now render
    recaptchaId = window.grecaptcha.render(htmlEl, captchaOptions, inheritFromDataAttr);
};

// this is assigned from "data-callback" or render()'s "options.callback"
window.onUserVerified = function (token) {
    alert('User Is verified');
    console.log('token=', token);
};


// click handler for form's submit button
function onSubmitBtnClick () {      
  var token =   window.grecaptcha.getResponse(recaptchaId);

  // if no token, mean user is not validated yet
  if (!token) {
     // trigger validation
     window.grecaptcha.execute(recaptchaId);
     return;
  }

  var xhrData = {
    'g-recaptcha-response': token
    // more ajax body/data here
  };

  // proceed with appending more ajax call data to xhrData and then rest of ajax call process
  // var xhr = new XMLHttpRequest();
  // ... ... .... ... ... 
}

#2


0  

You can easily validate google recaptcha's using jquery

您可以使用jquery轻松验证谷歌recaptcha

<?php 
$remoteip = $_SERVER['REMOTE_ADDR'];
?>
<script type="text/javascript">
    function reloadRecaptcha() {
        var publicKey = "your_public_key";
        var div = "recap";
        Recaptcha.create(publicKey,div,{theme: "white"});
        return false;
    }
    function validate() {

        var challenge = Recaptcha.get_challenge();
        var response = Recaptcha.get_response();
        var remoteip = "<?php echo $remoteip; ?>";
        $.ajax({
          type: "POST",
          url: "validateRecaptcha.php",
          async: false,
          data: {
            remoteip: remoteip,
            challenge: challenge,
            response: response
          },
          success: function(resp) {
                if(resp == "true") {
                    document.getElementById("message").innerHTML = "Perfect!";
                }
                else {
                    document.getElementById("message").innerHTML = "Incorrect Recaptcha! Please try again!";
                    reloadRecaptcha();
                }
          }
        });
        return false;
    }