Bootstrap v4 form validation with reCaptcha v3

穿精又带淫゛_ 提交于 2021-02-11 17:28:12

问题


I have a form that uses the bootstrap code below for form field validation. How do I integrate reCaptcha v3 to run after Bootstrap form validation? The reCaptcha code seems to override the validation.

reCaptcha v3 code

    grecaptcha.ready(function() {
    grecaptcha.execute('reCAPTCHA_site_key', {action: 'submit'}).then(function(token) {
    }); 

Bootstrap 4 form validation code

    (function () {
    "use strict";
    window.addEventListener(
        "load",
        function () {
            // Fetch all the forms we want to apply custom Bootstrap validation styles to
            var forms = document.getElementsByClassName("needs-validation");
            // Loop over them and prevent submission
            var validation = Array.prototype.filter.call(forms, function (form) {
                form.addEventListener(
                    "submit",
                    function (event) {
                        if (form.checkValidity() === false) {
                            event.preventDefault();
                            event.stopPropagation();


                        }
                        form.classList.add("was-validated");
                    },
                    false
                );
            });
        },
        false
    );
})(); 

回答1:


The answer is blood, sweat and tests.. about 1000 tests - I'll link to a gist with a fuller code example and add key points here.

This is code from a custom built registration page on a WordPress site using recaptcha v3 invisible check - so you will need to get some of the data from different sources, if you use-case is different.

Pseudo example: https://gist.github.com/qstudio/a4f70e4bdf6594e22996c1d9d51ec1b2

The key points are:

  • include the recaptcha scripts, passing a valid secret key
  • build the form inputs correctly, so they trigger validation
  • add a hidden field with the action - for extra security and metrics
  • include a js validation script, which you add the grecaptcha.execute() to if the validation tests pass
  • pass field values from form to data processor
  • then post data to PHP to scrutinize - validate each required part is there and as expected
  • proceed or kick back depending on the results and level of security you want

In the PHP processing part, I show how you can validate based on 4 possible values:

  • success => generic pass value
  • action => simple comparison to check the 2 values match
  • score => recaptcha returns a float value score, you can accept values above a defined threshold
  • hostname => another check if they match, optional and perhaps prone to issues.

Pseudo code included below - note this will not copy paste and work, but highlights the key pain-points.

Public Form

Build out all required form fields and add recaptcha , passing "key":

_form.html

<script src="https://www.google.com/recaptcha/api.js?render={{ recaptcha_key }}"></script>
<div class="g-recaptcha" id="g-recaptcha"
    data-sitekey="{{ recaptcha_key }}"
    data-size="invisible">
</div>
<form name="register" id="register" action="" method="post" class="needs-validation" novalidate>
        
    <!-- // form inputs here with validation feedback // -->
    
    <input type="text" placeholder="Username" name="user_login" id="user_login" value="" class="form-control" aria-describedby="inputGroupPrepend" required>
    <div class="valid-feedback">
        Username looks good :)
    </div>
    <div class="invalid-feedback">
        Sorry, no anonymity allowed ;)
    </div>

    <input type="hidden" name="q_action" id="q_action" value="register"/>
    <input type="hidden" name="q_registration_nonce" value="{{ q_registration_nonce }}"/>
        
</form>

JS Validation

Include bootstrap validation

_bootstrap-validation.js

// BS Form validation
(function() {
    'use strict';
    window.addEventListener('load', function() {
        
        // Fetch all the forms we want to apply custom Bootstrap validation styles to
        var forms = document.getElementsByClassName('needs-validation');
          
        // Loop over them and prevent submission
        var validation = Array.prototype.filter.call(forms, function(form) {
        
            form.addEventListener('submit', function(event) {
                
                if (
                    form.checkValidity() === false
                ) {

                    event.preventDefault();
                    event.stopPropagation();

                    // mark as validated ##
                    form.classList.add('was-validated');

                    return;

                } 

                // mark as validated ##
                form.classList.add('was-validated');

                // console.dir( validation );

                // declare vars ##
                var sitekey;
                var q_action;

                // check if recaptcha is enabled, if so, fire off ##
                if ( 
                    null !== document.getElementById( 'g-recaptcha' ) 
                    && null !== document.getElementById( 'g-recaptcha' ).getAttribute("data-sitekey")
                    && null !== document.getElementById( 'q_action' ) 
                ) {

                    // get action ##
                    q_action = document.getElementById( 'q_action' ).value;
                    // console.log( 'recaptcha action: '+q_action );

                    // get sitekey ##
                    sitekey = document.getElementById( 'g-recaptcha' ).getAttribute("data-sitekey");
                    // console.log( 'recaptcha key: '+sitekey );

                    grecaptcha.ready(function () {
                        grecaptcha.execute( sitekey, { action: q_action }).then(function ( token ) {
                            
                            // console.log( 'recapatcha token: '+token );
                            
                            var input = document.createElement( 'input' );// prepare a new input DOM element
                            input.setAttribute( 'name','q_recaptcha' ); // set the param name
                            input.setAttribute( 'value', token ); // set the value
                            input.setAttribute( 'type', 'hidden' ) // set the type, like "hidden" or other
                        
                            form.appendChild(input); // append the input to the form

                            // confirm ##
                            q_snack({
                                content:    'Houston... Tranquility base here. The Eagle has landed.', // msg ##
                                timeout:    -1, // never timeout ##
                                style:      'warning'
                            });
                            
                            // in case we go again ..
                            // grecaptcha.reset();

                            if (
                                form.checkValidity() === true
                            ) {

                                // console.log( 'OK to submit..' );

                                // submit ##
                                form.submit();

                            }

                        });
                    });
                }

            }, false);
        });
    }, false);
})();

Process $_POST submission

In this example, for a WordPress registation page, the data is process in PHP

_register.php

protected static function validate(){

    // h::log( 'e:>validate Registration... ' );
    h::log( $_POST );

    // check to make sure user registration is enabled
        if ( 
        ! \get_option( 'users_can_register' ) 
        || false == \get_option( 'users_can_register' )
    ){

        self::$code = 'f10';

        return false;

    }

    if ( 
        ! isset( $_POST['q_registration_nonce'] )
        || ! \wp_verify_nonce( $_POST['q_registration_nonce'], 'q_registration_nonce') 
    ){

        self::$code = 'f3';

        return false;

    }
        
    // no recaptcha ##
    if ( 
        ! isset( $_POST['q_recaptcha'] )
    ){

        self::$code = 'f12';

        return false;

    }

    // get the action ##
    $action = \sanitize_text_field( $_POST['q_action'] );
    // h::log( 'action: '.$action );

    // check if reCaptcha has been validated by Google      
    $recaptcha_secret = \apply_filters( "q/google/recaptcha/secret", false );
    // h::log( '$secret: '.$recaptcha_secret );

    // prepare the remote post ##
    $recaptcha_post = urlencode( \sanitize_text_field( $_POST['q_recaptcha'] ) );
    // h::log( '$recaptcha_post: '.$recaptcha_post );

    // get the host name without any protocol ##
    $host_name = str_replace( [ 'http://', 'http://www.', 'www.'  ], '', \get_site_url() );
    // h::log( '$host_name: '.$host_name );
         
    // sends post request to the URL and tranforms response to JSON
    $recaptcha_response = json_decode(
        file_get_contents(
            'https://www.google.com/recaptcha/api/siteverify?secret='.$recaptcha_secret.'&response='.$recaptcha_post
        )
    );

    // h::log( $recaptcha_response );
         
    // check recaptcha ##
    if( 
        $recaptcha_response->success === false // straight fail ##
        || $recaptcha_response->action !== $action // action miss=match ##
        || $recaptcha_response->score <= 0.5 // set your own tolerance.. float compare .. score too low.. reject ##
        || $recaptcha_response->hostname !== $host_name // validate hostname match ##
    ){

        self::$code = 'f11';

        return false;

    }

    if ( 
        ! isset( $_POST['user_email'] )
        || ! isset( $_POST['user_login'] )
    ){

        self::$code = 'f4';

        return false;

    }

    // get details + sanitize ##
    $user_login        = \sanitize_user( $_POST["user_login"] );    
    $user_email        = \sanitize_email( $_POST["user_email"] );
        
    // from here, you can do what you need to to validate the passed data .... 
        
}


来源:https://stackoverflow.com/questions/62333907/bootstrap-v4-form-validation-with-recaptcha-v3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!