laravel 使用 ReCaptcha v3 时出现“超时或重复”错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/55308499/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 18:18:20  来源:igfitidea点击:

I got "timeout-or-duplicate" error using ReCaptcha v3

phplaravelformsrecaptcharecaptcha-v3

提问by Saku

I got a contact form on my website on Laravel and I'd like to place a ReCaptcha v3 but for now the result I got from the verification is the error "timeout-or-duplicate".

我在 Laravel 的网站上有一个联系表格,我想放置一个 ReCaptcha v3,但现在我从验证中得到的结果是错误“超时或重复”。

Can you help me from A to Z ? I don't know where to go...

你能帮我从头到尾吗?我不知道去哪里...

My head :

我的头 :

<script src="https://www.google.com/recaptcha/api.js?render=My_Site_Key"></script>
  <script>
    grecaptcha.ready(function () {
      grecaptcha.execute('My_Site_Key', { action: 'contact' }).then(function (token) {
        var recaptchaResponse = document.getElementById('recaptchaResponse');
          recaptchaResponse.value = token;
      });
    });
  </script>

The contact form :

联系方式:

<form action="{{ route('contact.post') }}" id="contact-form" method="post" name="contactForm">
   <input type="hidden" name="_token" id="token" value="{{ csrf_token() }}">
   <input type="hidden" name="recaptcha_response" id="recaptchaResponse">
   <fieldset>
     <div class="col-sm-12">
       <input id="name" name="name" placeholder="Nom*" type="text">
     </div>
     <div class="col-sm-12">
       <input id="email" name="email" placeholder="Email*" type="text">
     </div>
     <div class="col-sm-12">
       <input id="object" name="object" placeholder="Objet*" type="text" autocomplete="off">
     </div>
     <div class="col-xs-12">
       <textarea cols="5" id="message" name="message" placeholder="Votre message...*"></textarea>
     </div>
     <div class="col-xs-12">
       <button class="submit active" id="contact-submit">ENVOYER</button>
     </div>
     <div class="error col-xs-12">
       <h3></h3>
     </div>
     <div class="success col-xs-12">
       <h3>Merci ! Votre message a été envoyé !</h3>
     </div>
   </fieldset>
</form>

Route:

路线:

Route::post('/contact', array('as' => 'contact.post', 'uses' => 'ContactController@send'));

The Contact Controller :

联系人控制器:

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Mail;

class ContactController extends Controller
{
    public function send() {
      $info = array(
          'name' => Input::get('name'),
          'email' => Input::get('email'),
          'object' => Input::get('object'),
          'message' => Input::get('message')
      );
      if($info['name'] == "" || $info['email'] == "" || $info['object'] == "" || $info['message'] == "") {
          return json_encode(['response' => 'Tous les champs doivent être remplis !']);
      }
      if(!filter_var($info['email'], FILTER_VALIDATE_EMAIL)) {
          return json_encode(['response' => 'Vous devez rentrer une adresse e-mail valide !']);
      }
      $ip = Request()->ip();

      // Build POST request:
      $recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
      $recaptcha_secret = 'My_Secret_Key';
      $recaptcha_response = $_POST['recaptcha_response'];
      // Make and decode POST request:
      $recaptcha = file_get_contents($recaptcha_url . '?secret=' . $recaptcha_secret . '&response=' . $recaptcha_response);
      $recaptcha = json_decode($recaptcha);
      // Take action based on the score returned:
      if ($recaptcha->score < 0.5) {
          return json_encode(['response' => 'Vous êtes considéré comme Bot/Spammer !', 'score' => $recaptcha->score]);
      }

      Mail::send(['email.html.contact', 'email.text.contact'], ['info' => $info, 'ip' => $ip], function($message) use ($info) {
          $message->to('[email protected]')->subject('Bryan Gossuin | Formulaire de contact');
          $message->replyTo($info['email'], $info['name']);
      });
      return json_encode(['response' => 'success','']);
  }
}

Finaly the javascript

最后是 javascript

      $('#contact-form').on('submit', function(e) {
          e.preventDefault();
          swal({
              title: "Souhaitez-vous vraiment envoyer ce mail ?",
              icon: "warning",
              buttons: {
                cancel: {
                  text: "Annuler",
                  value: false,
                  visible: true,
                  closeModal: true,
                },
                confirm: "Envoyer",
              }
            })
            .then((value) => {
              if (value) {
                  $.ajax({
                          method: "POST",
                          url: "contact",
                          cache: false,
                          data: $(this).serialize(),
                          dataType: 'json',
                          success: function(json) {
                              console.log(json.score);
                              if (json.response == 'success') {
                                  $('#contact-form').trigger("reset");
                                  swal("E-mail envoyé", "Merci de votre demande !", "success");
                              } else {
                                  swal("Erreur !", json.response, "error");
                              }
                          }
                      }
                  )
               }
            });
      });

The output I got from google is

我从谷歌得到的输出是

{
  "success": false,
  "error-codes": [
    "timeout-or-duplicate"
  ]
}

and I expect it to be

我希望它是

{
  "success": true,
  "score" : x,
  "error-codes": '',
}


I guess the problem is because the ??method post?? is used two times because when I Check directly On the API Google to verify the user token it show le thé code but right after I refresh the page it show me ??timeout or duplicate?? but I dont know how to fix this

我猜问题是因为 ??method post?? 使用了两次,因为当我直接在 API 上检查 Google 以验证用户令牌时,它会显示 le 代码,但在我刷新页面后,它会显示给我?超时或重复?但我不知道如何解决这个问题

回答by Nihar Sarkar

Every time the page reloads you get a new token from google . You can use that token only once . Somehow if you are using that token more than once to get the response from google Api , you will get that error . Check this error reference https://developers.google.com/recaptcha/docs/verify?hl=en

每次页面重新加载时,您都会从 google 获得一个新令牌。您只能使用该令牌一次。不知何故,如果您多次使用该令牌来获取来自 google Api 的响应,您将收到该错误。检查此错误参考https://developers.google.com/recaptcha/docs/verify?hl=en

回答by Jorge

As stated in the documentationthis error is caused by:

文档中所述,此错误是由以下原因引起的:

  1. Validity time of the token expired (After you get the response token, you need to verify it within two minutes)
  2. Token has been used previously. To confirm that, log the token value before is used (error log, local file, whatever)
  1. 令牌有效期已过(收到响应令牌后,需要在两分钟内进行验证)
  2. 之前已经使用过令牌。要确认这一点,请在使用之前记录令牌值(错误日志、本地文件等)

My resolution for 1, set an interval that calls the set token function, so it is refreshed every 2 minutes.

我的分辨率为1,设置一个调用set token函数的间隔,所以每2分钟刷新一次。

$(document).ready(function() {

            SetCaptchaToken();
            setInterval(function () { SetCaptchaToken(); }, 2 * 60 * 1000);

    });

Resolution for 2, fix your code :)

解决方案 2,修复您的代码 :)

回答by Matthew Lock

I got this from people double clicking the submit button on the form.

我从人们双击表单上的提交按钮那里得到了这个。

回答by Adam

The problem is this piece of code:

问题是这段代码:

<script src="https://www.google.com/recaptcha/api.js?render=My_Site_Key"></script>
  <script>
    grecaptcha.ready(function () {
      grecaptcha.execute('My_Site_Key', { action: 'contact' }).then(function (token) {
        var recaptchaResponse = document.getElementById('recaptchaResponse');
          recaptchaResponse.value = token;
      });
    });
  </script>

The token is only valid for 2 minutes after you executeis called as stated in the docs:

文档中execute所述,令牌仅在您被调用后 2 分钟内有效:

Note: reCAPTCHA tokens expire after two minutes. If you're protecting an action with reCAPTCHA, make sure to call execute when the user takes the action.

注意:reCAPTCHA 令牌在两分钟后过期。如果您使用 reCAPTCHA 保护操作,请确保在用户执行操作时调用 execute。

Thus, if you spend more then 2 minutes on the contact-form, you get the timout error,. Thats why its recommended in the docs to only call executeif the user actually submits your form / takes action. In vanilla JS it would look like this:

因此,如果您在联系表单上花费的时间超过 2 分钟,则会出现超时错误。这就是为什么它在文档中建议仅execute在用户实际提交您的表单/采取行动时才调用。在 vanilla JS 中,它看起来像这样:

<script src="https://www.google.com/recaptcha/api.js?render=My_Site_Key"></script>
<script>
  grecaptcha.ready(function() {
      document.getElementById('contact-form').addEventListener("submit", function(event) {
        event.preventDefault();
        grecaptcha.execute('My_Site_Key', {action: 'contact'}).then(function(token) {
           document.getElementById("recaptchaResponse").value= token; 
           document.getElementById('contact-form').submit();
        });
      }, false);
  });
</script>