Как подружить Капчу Yandex API и AJAX

от автора

Здравствуйте, уважаемые коллеги. Споры о том, нужна ли так называемая «капча», приносит ли она реальную пользу в деле борьбы со злобными роботами или только вредит «юзабилити» проекта давно утихли, и каждый, кто так или иначе интересовался этим вопросом сделал для себя соответствующие выводы.

Тем не менее, столкнувшись с необходимостью установить капчу в форму авторизации для очередного проекта, а так же после нескольких часов возни с сервисом reCaptcha, который генерирует на странице тонны мусорного кода, я так и не нашёл готового решения, которое бы устроило меня на сто процентов. Ну что же, если хочешь что-то сделать — сделай это сам.

В данной статье речь пойдёт о превращении простого и удобного API Яндекс — Чистый Веб в полноценную, современную и функциональную капчу. А раз уж мы заговорили о модуле авторизации, то думаю, что уместно будет показать — как наша новая капча работает в связке с модулем.

Итак, нам понадобится:
API Яндекс — Чистый Веб.

Думаю что любителям нативного js больше ничего и не понадобиться, я же использовал библиотеку jQuery

Первым делом обратимся к Yandex API, ведь сперва нам нужно получить искомую капчу. Почитав документацию пишем класс, который собственно её и отдаёт:

class_yandex_capcha.php

class 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;     } } 

Всё предельно просто — отправляем GET запрос с параметрами:
$key — Яндекс API Ключ, Получить можно тут
$type — тип капчи, которую хотим получить, значения, принимаемые переменной всецело описаны в документации Яндекса.

В моём случае сайт поддерживает несколько языков — поэтому в зависимости от языка выбираем: std — цифры и логотип Яндекса на русском, либо estd — тоже цифры, но с логотипом на английском.

Метод yandexCaptcha::get() теперь возвращает адрес изображения — это и есть наша капча.
Кроме «url картинки капчи» xml запрос метода возвращает ещё один параметр — captcha, но есть маленькая хитрость благодаря которой
параметр captcha можно не хранить, скажем в сессии. Его вообще можно не хранить, почему — объясню немного дальше.

Когда наш класс готов — встраиваем его в модель страницы авторизации на сайте:

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;         }     }      

Последние три строки метода get_data() отдают картинку капчи представлению:

А вот собственно и представление — 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="<?php echo $data['capcha_url']; ?>" 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> 

Как видно из представления — в html коде нет тегов form, а это значит, что скорее всего для авторизации мы будем использовать AJAX

В представлении у нас 3 поля: логин, пароль и поле для ввода капчи, сама картинка капчи и кнопка отправить.

Отправлять форму, как уже было сказано ранее, мы будем с помощью AJAX запроса в login.js

Много кода, но именно тут — вся суть

$(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();             }         }     }); }); 

Теперь нам нужно проверить капчу.
Для этого из полей нашей формы по клику на кнопку «отправить» либо по кнопке Enter (любим пользователей), хватаем все данные — логин, пароль, набор символов введённый пользователем в поле для ввода капчи, и ещё один параметр, о котором я писал выше — тот самый параметр captcha (смотри описание метода yandexCaptcha::get()).
Дело в том, что этот параметр — ключевой для проверки правильности ввода капчи пользователем изначально присутствует на странице как часть URL картинки капчи. Её отдаёт нам Яндекс всё в том же методе yandexCaptcha::get(). Нам остаётся только «вычленить» параметр captcha из url адреса изображения, что мы и делаем.

Файл module_login.php которому мы передаём данные с помощью AJAX запроса, создаёт экземпляр класса Login — основного класса используемого нами для авторизации пользователей на сайте, вызывает метод siteLogin() который возвращает данные о результате авторизации, проверив перед этим правильность комбинации логин-пароль и, что нас интересует больше всего — правильность ввода капчи.

    echo Login::siteLogin($login, $passworld, $captchaCode, $captchaValue); 

Чтобы моя статья, которая и так уже получилась намного длиннее чем я планировал не разрасталась до совершенно громадных размеров, приведу только ту часть метода Login::siteLogin который отвечает за проверку капчи:

class_login.php

Собственно код

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) {                     //Ура! Авторизация прошла успешно                     }             }         }         return json_encode($json);     } } 

*Подумал и решил привести весь код, чтобы читателю было понятнее откуда растут ноги.
Суть проверки капчи — снова GET запрос согласно Yandex API;

Соответственно — вели верную комбинацию логин/пароль + капча прошла проверку — Ура! Мы прошли авторизацию.

Дальше, думаю будет наиболее важный момент статьи. Обращаю внимание тех, кто всё ещё читает — всё сводиться к архитектуре приложения.

Предположим пользователь ошибся при вводе капчи, либо просто не может разобрать текст на картинке и хочет её обновить.

Внимание — ему не нужно совершать никаких лишних телодвижений

В случае ошибки — введённые пользователем ранее логин и пароль сохраняются, а картинка капчи автоматически обновляется. А если пользователь решит обновить капчу — ему достаточно просто кликнуть на неё.

Вернёмся к содержимому файла login.js, и рассмотрим функцию capchaRenew()

 function capchaRenew() {         $.ajax({             type: "POST",             data: {check:"ok"},             url: "/app/modules/module_capcha_renew.php"         }).done(function(html) {             $(".yandexCapchaImage").attr("src",html);         });     } 

Итак — мы просто делаем запрос к модулю module_capcha_renew.php вся функция которого сводится к тому,
чтобы переформировать капчу с помощью того же метода yandexCaptcha::get() к которому мы обращались при загрузке страницы и отдать адрес нового изображения пользователю.

В итоге — имеем полнофункциональную капчу, достаточно дружественную пользователю. А главное, в отличии от того же самого reCapcha — нет никаких iframe, весь код довольно лаконичен, и полностью находится под нашим контролем.

ссылка на оригинал статьи http://habrahabr.ru/post/205456/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *