Здравствуйте, уважаемые коллеги. Споры о том, нужна ли так называемая «капча», приносит ли она реальную пользу в деле борьбы со злобными роботами или только вредит «юзабилити» проекта давно утихли, и каждый, кто так или иначе интересовался этим вопросом сделал для себя соответствующие выводы.
Тем не менее, столкнувшись с необходимостью установить капчу в форму авторизации для очередного проекта, а так же после нескольких часов возни с сервисом reCaptcha, который генерирует на странице тоннымусорного кода, я так и не нашёл готового решения, которое бы устроило меня на сто процентов. Ну что же, если хочешь что-то сделать — сделай это сам.
В данной статье речь пойдёт о превращении простого и удобного API Яндекс — Чистый Веб в полноценную, современную и функциональную капчу. А раз уж мы заговорили о модуле авторизации, то думаю, что уместно будет показать — как наша новая капча работает в связке с модулем.
Итак, нам понадобится:
API Яндекс — Чистый Веб.
Думаю что любителям нативного js больше ничего и не понадобиться, я же использовал библиотеку jQuery
Первым делом обратимся к Yandex API, ведь сперва нам нужно получить искомую капчу. Почитав документацию пишем класс, который собственно её и отдаёт:
class_yandex_capcha.php
Всё предельно просто — отправляем GET запрос с параметрами:
$key — Яндекс API Ключ, Получить можно тут
$type — тип капчи, которую хотим получить, значения, принимаемые переменной всецело описаны в документации Яндекса.
В моём случае сайт поддерживает несколько языков — поэтому в зависимости от языка выбираем: std — цифры и логотип Яндекса на русском, либо estd — тоже цифры, но с логотипом на английском.
Метод yandexCaptcha::get() теперь возвращает адрес изображения — это и есть наша капча.
Кроме «url картинки капчи» xml запрос метода возвращает ещё один параметр — captcha, но есть маленькая хитрость благодаря которой
параметр captcha можно не хранить, скажем в сессии. Его вообще можно не хранить, почему — объясню немного дальше.
Когда наш класс готов — встраиваем его в модель страницы авторизации на сайте:
model_closed.php
Последние три строки метода get_data() отдают картинку капчи представлению:
А вот собственно и представление — closed_view.php
Как видно из представления — в html коде нет тегов form, а это значит, что скорее всего для авторизации мы будем использовать AJAX.
В представлении у нас 3 поля: логин, пароль и поле для ввода капчи, сама картинка капчи и кнопка отправить.
Отправлять форму, как уже было сказано ранее, мы будем с помощью AJAX запроса в login.js
Теперь нам нужно проверить капчу.
Для этого из полей нашей формы по клику на кнопку «отправить» либо по кнопке Enter (любим пользователей), хватаем все данные — логин, пароль, набор символов введённый пользователем в поле для ввода капчи, и ещё один параметр, о котором я писал выше — тот самый параметр captcha (смотри описание метода yandexCaptcha::get()).
Дело в том, что этот параметр — ключевой для проверки правильности ввода капчи пользователем изначально присутствует на странице как часть URL картинки капчи. Её отдаёт нам Яндекс всё в том же методе yandexCaptcha::get(). Нам остаётся только «вычленить» параметр captcha из url адреса изображения, что мы и делаем.
Файл module_login.php которому мы передаём данные с помощью AJAX запроса, создаёт экземпляр класса Login — основного класса используемого нами для авторизации пользователей на сайте, вызывает метод siteLogin() который возвращает данные о результате авторизации, проверив перед этим правильность комбинации логин-пароль и, что нас интересует больше всего — правильность ввода капчи.
Чтобы моя статья, которая и так уже получилась намного длиннее чем я планировал не разрасталась до совершенно громадных размеров, приведу только ту часть метода Login::siteLogin который отвечает за проверку капчи:
class_login.php
*Подумал и решил привести весь код, чтобы читателю было понятнее откуда растут ноги.
Суть проверки капчи — снова GET запрос согласно Yandex API;
Соответственно — вели верную комбинацию логин/пароль + капча прошла проверку — Ура! Мы прошли авторизацию.
Дальше, думаю будет наиболее важный момент статьи. Обращаю внимание тех, кто всё ещё читает — всё сводиться к архитектуре приложения.
Предположим пользователь ошибся при вводе капчи, либо просто не может разобрать текст на картинке и хочет её обновить.
Внимание — ему не нужно совершать никаких лишних телодвижений
В случае ошибки — введённые пользователем ранее логин и пароль сохраняются, а картинка капчи автоматически обновляется. А если пользователь решит обновить капчу — ему достаточно просто кликнуть на неё.
Вернёмся к содержимому файла login.js, и рассмотрим функцию capchaRenew()
Итак — мы просто делаем запрос к модулю module_capcha_renew.php вся функция которого сводится к тому,
чтобы переформировать капчу с помощью того же метода yandexCaptcha::get() к которому мы обращались при загрузке страницы и отдать адрес нового изображения пользователю.
В итоге — имеем полнофункциональную капчу, достаточно дружественную пользователю. А главное, в отличии от того же самого reCapcha — нет никаких iframe, весь код довольно лаконичен, и полностью находится под нашим контролем.
Тем не менее, столкнувшись с необходимостью установить капчу в форму авторизации для очередного проекта, а так же после нескольких часов возни с сервисом 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="" 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, весь код довольно лаконичен, и полностью находится под нашим контролем.