Dear colleagues. Disputes about whether the so-called “captcha” is needed, whether it brings real benefits in the fight against evil robots, or just harms the project’s usability has long subsided, and everyone who in one way or another was interested in this issue drew the appropriate conclusions for themselves.
However, faced with the need to install a captcha in the authorization form for the next project, and after a few hours of fussing with the reCaptcha service, which generates tons of
garbage code on the page, I still could not find a ready solution that would suit me one hundred percent . Well, if you want to do something - do it yourself.
This article focuses on the transformation of a simple and convenient API Yandex - Clean Web into a full-fledged, modern and functional captcha. And since we are talking about the authorization module, I think it would be appropriate to show how our new captcha works in conjunction with the module.
So, we will need:
API Yandex - Net Web .
')
I think that lovers of native js will not need anything else, I used the
jQuery library
First we turn to the Yandex API, because first we need to get the desired captcha. After reading the documentation we write the class, which actually gives it to:
class_yandex_capcha.phpclass yandexCaptcha { static function get() { $lang = $_SESSION['lang']; if ($lang == "ru") { $type = "std"; } else { $type = "estd"; } $key = " API "; $xmlResponse = file_get_contents("http://cleanweb-api.yandex.ru/1.0/get-captcha?key=".$key."&type=".$type); $xml = simplexml_load_string($xmlResponse); return $xml->url; } }
Everything is extremely simple - send a GET request with parameters:
$ key - Yandex API Key,
You can get it here.$ type is the type of captcha that we want to receive, the values taken by the variable are fully described in the Yandex documentation.
In my case, the site supports several languages - therefore, depending on the language, we select:
std - numbers and the Yandex logo in Russian, or
estd - also numbers, but with a logo in English.
The
yandexCaptcha :: get () method now returns the image address - this is our captcha.
In addition to the "url image captcha" xml method request returns another parameter -
captcha , but there is a little trick thanks to which
the captcha parameter can be not stored, say in a session. It can not be stored at all, why - I will explain a little further.
When our class is ready - we build it into the model of the authorization page on the site:
model_closed.php class model_closed extends model { function get_data() { $root = $_SERVER['DOCUMENT_ROOT']; $dataArray['language'] = parse_ini_file($root."/app/languages/".Route::$lang."_closed.ini"); $dataArray['base_href'] = $_SERVER['HTTP_HOST']; require_once($root."/app/core/class/class_yandex_capcha.php"); $dataArray['capcha_url'] = yandexCaptcha::get(); return $dataArray; } }
The last three lines of the
get_data () method give the captcha picture to the view:
But actually the presentation -
closed_view.php <div class="closedLoginForm"> <input type="text" class="loginInput" value="<?php echo $data['language']['login']; ?>"><br> <input type="password" class="passworldInput" value="<?php echo $data['language']['passworld']; ?>"> <div id="capcha" title="<?php echo $data['language']['reload_image']; ?>"> <img class="yandexCapchaImage" src="" alt="<?php echo $data['language']['capcha']; ?>"> </div> <input type="text" class="capchaInput" value="<?php echo $data['language']['capcha']; ?>"> <div class="loginButton"><div class="loginBottonInner"><?php echo $data['language']['loginBotton']; ?></div></div> <div class="loginError"></div> </div> <script type="text/javascript" src="/js/jquery.js"></script> <script type="text/javascript" src="/js/closed.js"></script> <script type="text/javascript" src="/js/login.js"></script>
As you can see from the presentation, there are no
form tags in the html code
, which means that
we will most likely
use AJAX for authorization .
In the view, we have 3 fields: a login, password, and a captcha field, a captcha picture itself, and a send button.
Send the form, as mentioned earlier, we will use the AJAX request in
login.jsA lot of code, but it is here - the whole point $(document).ready(function(){ var capchaStartText = $(".capchaInput").val(); var passworldStartText = $(".passworldInput").val(); function login() { var login = $(".loginInput"), passworld = $(".passworldInput"), capcha = $(".capchaInput"), buttontext = $(".loginBottonInner").html(), captchasrc = $(".yandexCapchaImage").attr("src"); var pos = captchasrc.indexOf("="); var key = captchasrc.substr(pos+1); $.ajax({ type: "POST", url: "/app/modules/module_login.php", dataType: "json", data: {login:login.val(), passworld:passworld.val(), captchaCode:key, captchaValue:capcha.val()}, beforeSend: function(){ $(".loginBottonInner").html("..."); } }).done(function(data){ if (data.captcha == 1 && data.login == 1) { location.reload(); } if (data.login == 0) { capchaRenew(); $(".loginBottonInner").html(buttontext); login.focus(); login.select(); passworld.val(passworldStartText); capcha.val(capchaStartText); $(".loginError").html(data.login_error); } if (data.captcha == 0 && data.login == 1) { capchaRenew(); $(".loginBottonInner").html(buttontext); capcha.val(capchaStartText); capcha.focus(); $(".loginError").html(data.captcha_error); } }); } function capchaRenew() { $.ajax({ type: "POST", data: {check:"ok"}, url: "/app/modules/module_capcha_renew.php" }).done(function(html) { $(".yandexCapchaImage").attr("src",html); console.log(html); }); } $("#capcha").click(function(){ capchaRenew(); $(".capchaInput").focus(); }); $(".loginBottonInner").click(function() { login(); }); $(window).keydown(function(eventObject){ if ($(".closedLoginForm input").is(":focus") == true) { if (eventObject.which == 13) { login(); } } }); });
Now we need to check the captcha.To do this, from the fields of our form by clicking on the “send” button or on the Enter button (we love users), we grab all the data - the login, password, character set entered by the user in the captcha field, and one more parameter I wrote about above - the same parameter
captcha (see the description of the method
yandexCaptcha :: get () ).
The fact is that this parameter is the
key for checking the correctness of captcha input by the user initially present on the page as part of the captcha image URL. Yandex gives her all in the same method
yandexCaptcha :: get () . We can only “isolate” the
captcha parameter from the url of the image address, which we do.
The
module_login.php file
to which we transfer data using an AJAX request, creates an instance
of the Login class — the main class we use to authorize users on the site; calls the
siteLogin () method that returns data on the result of the authorization, checking the correctness of the login-password combination and, What interests us most is the
correctness of captcha input. echo Login::siteLogin($login, $passworld, $captchaCode, $captchaValue);
So that my article, which already turned out to be much longer than I had planned, did not grow to a completely enormous size, I will give only that part of the
Login :: siteLogin method that is responsible for checking the captcha:
class_login.phpActually code class Login { static function siteLogin($login, $password, $captchaCode, $captchaValue) { $path = $_SERVER['DOCUMENT_ROOT']; session_start(); $langArray = parse_ini_file($path."/app/languages/".$_SESSION['lang']."_login.ini"); $login = htmlspecialchars($login); $password = htmlspecialchars($password); $json = Array(); $json['captcha'] = 0; $json['login'] = 0; $json['captcha_error'] = $langArray['captcha_error']; $json['login_error'] = $langArray['login_error']; $key = " API "; $response = file_get_contents("http://cleanweb-api.yandex.ru/1.0/check-captcha?key=".$key."&captcha=".$captchaCode."&value=".$captchaValue); if (strpos($response,"<ok")){ $json['captcha'] = 1; unset($json['captcha_error']); } require_once $path."/app/core/class/class_dbconnect.php"; $mysqli = dbconnect::connect(); $sql = "SELECT `id`,`login`,`passworld`,`rights` FROM `users` WHERE `login` = '".$login."'"; $qr = $mysqli->query($sql); $quant = $qr->num_rows; if ($quant <> 0) { $row = $qr->fetch_assoc(); if ($row['passworld'] == hash("whirlpool","super".$password."orgy")) { $json['login'] = 1; unset($json['login_error']); if ($json['login'] == 1 and $json['captcha'] == 1) {
* I thought about it and decided to bring all the code so that the reader could understand where the legs grow from.
The essence of captcha checking is again a GET request according to the Yandex API;
Accordingly, they led the right combination of login / password + captcha passed the test - Hooray! We are authorized.
Further, I think there will be the
most important moment of the article . I draw the attention of those who are still reading - it all comes down to the architecture of the application.
Suppose the user made a mistake when entering a captcha, or simply cannot parse the text in the picture and wants to update it.
Warning - he does not need to make any extra gesturesIn case of an error, the username and password entered by the user earlier are saved, and the captcha image is automatically updated. And if the user decides to update the captcha - it is enough for him to simply click on it.
Let's
go back to the contents of the
login.js file, and look at the
capchaRenew () function
. function capchaRenew() { $.ajax({ type: "POST", data: {check:"ok"}, url: "/app/modules/module_capcha_renew.php" }).done(function(html) { $(".yandexCapchaImage").attr("src",html); }); }
So - we just make a request to the module
module_capcha_renew.php the whole function of which comes down to
to reshape the captcha using the same method
yandexCaptcha :: get () that we addressed when loading the page and give the address of the new image to the user.
As a result, we have a fully functional captcha, user friendly enough. And most importantly, unlike the same reCapcha, there are no iframes, all the code is rather laconic, and is completely under our control.