问题
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