Тем не менее, столкнувшись с необходимостью установить капчу в форму авторизации для очередного проекта, а так же после нескольких часов возни с сервисом 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/
Добавить комментарий