Добавляем CAPTCHA к форме. Пример PHP скрипта, использующего AJAX.

23 февраля, 2008

Логотип CAPTCHA
О том, что такое CAPTCHA и зачем она нужна, думаю рассказывать не нужно. Все, кто хоть раз регистрировался на каком-нибудь интернет-сервисе, прекрасно понимают, о чем идет речь.

В этой статье мы посмотрим, как создать несложную CAPTCHA, добавить ее к форме, и проверить правильно ли посетитель ввел ее значение. Посмотреть действующий пример можно здесь.

Сразу хочу отметить, что речь пойдет именно о работе с CAPTCHA, а не о создании рисунка для нее.

Поэтому мы используем готовую библиотеку для создания рисунка CAPTCHA. Библиотека называется достаточно незамысловато – captcha и может использоваться вместе с фрэймворком CodeIgniter. В описании библиотеки приведен пример ее использования на обычной странице, поэтому мы рассмотрим вариант проверки с применением технологии ajax.

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

Разница заключается в том, что в случае captcha посетитель должен ввести заранее определенное значение (обычно оно нарисовано на картинке). Значит, мы должны каким-то образом сохранить это значение, а затем использовать для проверки.

Для хранения данных captcha мы используем базу данных, а отличать посетителей друг от друга будем по их ip адресу.

Примечание. Это, конечно, не единственное решение.

Т.к. в этом примере мы используем CodeIgniter, то предположим, что у нас есть контроллер (captchademo.php), который содержит два метода: index() (создает страницу с формой) и ajaxcheck() (выполняет проверку и обработку данных формы). Отправку ajax запроса и обработку его результатов будут выполнять функции, размещенные в checkcaptcha.js. Чтобы сократить количество JavaScript кода, используем библиотеку prototype.

Принцип работы captcha показан на диаграмме.

Диаграмма работы с CAPTCHA (миниатюра)

Рассмотрим его немного подробнее.

При входе на страницу с формой, выполняется метод index контроллера, который создает страницу с формой и отправляет ее браузеру.

  1. function index() {
  2.     $this->load->library('captcha');
  3.     //создаем captcha
  4.     $vals = array(
  5.         'word' => mt_rand(0, 9999),
  6.         'img_path' => str_replace(SELF, "", FCPATH).'uploads'.DIRECTORY_SEPARATOR,
  7.         'img_url' => base_url().'uploads/',
  8.         'font_path' => str_replace(SELF, "", FCPATH).'system'.DIRECTORY_SEPARATOR.'fonts'.DIRECTORY_SEPARATOR.'AntsyPants.ttf',
  9.         'img_width' => '100',
  10.         'img_height' => '30',
  11.         'expiration' => '7200'
  12.     );
  13.     $cap = $this->captcha->create_captcha($vals);
  14.     //добавляем запись с данными captcha в БД
  15.     $data = array(
  16.         'captcha_id' => ",
  17.         'captcha_time' => $cap['time'],
  18.         'ip_address' => $this->input->ip_address(),
  19.         'word' => $cap['word']
  20.     );
  21.     $query = $this->db->insert_string('captcha', $data);
  22.     $this->db->query($query);
  23.  
  24.     $pageData['title'] = "Тестирование CAPTCHA";
  25.     $pageData['scripts'] = array('js/prototype.js', 'js/checkcaptcha.js');
  26.     $pageData['image'] = $cap['image'];
  27.     $this->load->view('header', $pageData);
  28.     $this->load->view('captcha');
  29.     $this->load->view('footer');
  30. }

В строках 2-13 мы загружаем библиотеку captcha и создаем массив с настройками.
word – текст, нарисованный на картинке;
img_path – путь к изображениям;
img_url – адрес изображений;
font_path – путь к файлу со шрифтом, который будет использоваться (/system/fonts/);
img_width – длина картинки;
img_height – высота картинки;
expiration – время «жизни» (в секундах).

Как видите, ничего сложного, самое главное правильно указать путь и адрес файлов с картинками (для этого использована функция base_url() и несколько констант, которые инициализируются в index.php).

Примечание. Для того, чтобы приведенный здесь код работал, библиотека captcha должна быть размещена в папке system/application/libraries либо system/libraries. Кроме того, необходимо создать таблицу в БД для хранения данных captcha:

  1. CREATE TABLE `captcha` (
  2.   `captcha_id` bigint(13) UNSIGNED NOT NULL AUTO_INCREMENT,
  3.   `captcha_time` int(10) UNSIGNED NOT NULL,
  4.   `ip_address` varchar(16) NOT NULL DEFAULT '0',
  5.   `word` varchar(20) NOT NULL,
  6.   PRIMARY KEY  (`captcha_id`),
  7.   KEY `word` (`word`)
  8. ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Обратите внимание. Мы задали все параметры явно, хотя большинство из них можно не задавать. Но оказалось, что значения по-умолчанию подобраны не лучшим образом и текст попросту не помещается на рисунке. Возможно, это зависит от используемых шрифтов, но все равно, стоит убедиться, что текст виден.

После того, как объект captcha создан, добавляем его данные в БД (строки 15-22).

А дальше – загружаем представление, которое содержит нашу форму. Обратите внимание, url рисунка передан в $pageData['image'] (строка 26). Таким образом, в представлении будет создана переменная $title с адресом рисунка.

Сама форма выглядит следующим образом:

  1. <form action="#" method="post">
  2. <p>
  3. <label for="checkcode">Введите число, изображенное на картинке *: </label>
  4. <input type="text" size="30" id="checkcode" />
  5. <?php echo $image; ?>
  6. </p>
  7. <p>
  8. <input type="button" value="Проверить" id="sendBtn" onclick="check('<?php echo site_url(); ?>', '<?php echo base_url(); ?>')" />
  9. </p>
  10. </form>
  11. <div id="res"></div>

Эта форма содержит только одно поле – для ввода captcha.

После нажатия на кнопку «Проверить» будет вызвана функция check (находится в файле checkcaptcha.js), которая в качестве параметров получит адрес сайта. Эта информация используется для отправки запроса и вставки анимации (о ней чуть ниже).

Примечание. В строках 27 и 29 метода index() мы загрузили представления с заголовком и хвостовиком страницы. Хвостовик не содержит ничего кроме закрывающих тегов, а вот заголовок загружает js библиотеки.

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN">
  2. <html xmlns="http://www.w3.org/1999/xhtml" lang="ru">
  3. <head>
  4. <title><?php echo $title; ?></title>
  5. <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  6. <link rel="stylesheet" type="text/css" href="<?php echo base_url(); ?>css/styles.css" />
  7. <?php
  8. if (isset($scripts)) {
  9.     foreach ($scripts as $script) {
  10.         echo "<script src=\"".base_url().$script."\" type=\"text/javascript\" />\n";
  11.     }
  12. }
  13. ?>
  14. </head>
  15. <body>

Теперь рассмотрим, функцию check, которая вызывается при нажатии на кнопку «Проверить».

  1. function check(siteUrl, baseUrl) {
  2.     var pars = $H({check:$('checkcode').value});
  3.     $('res').innerHTML = '<img src=\"' + baseUrl + '/css/images/ajax-loader.gif' + '\" alt=\"Отправка данных…\" />';
  4.     $('sendBtn').style.display = "none";
  5.     new Ajax.Request(
  6.         siteUrl + "/captchademo/ajaxcheck",
  7.         {
  8.             parameters:pars.toQueryString(),
  9.             onComplete: function(transport) {
  10.                 var response = eval("(" + transport.responseText + ")");
  11.                 $('sendBtn').style.display = "";
  12.                 if (response.state == "OK") {
  13.                     $("res").innerHTML = "Правильно!<br />";
  14.                 }
  15.                 else {
  16.                     $("res").innerHTML = response.errMessages;
  17.                 }
  18.             },
  19.             onFailure: function() {
  20.                 alert("Error");
  21.             }
  22.         }
  23.     );
  24. }

В строке 2 мы читаем введенное значение.

После этого, прячем кнопку «Проверить» и вставляем картинку с анимацией (строки 2 и 3).

Примечание. Я сделал картинку с анимацией с помощью сервиса www.ajaxload.info.

В строке 5 создаем и отправляем запрос.

Если запрос выполнен успешно, то для обработки его результатов будет вызвана анонимная функция, определенная в параметре onComplete (строки 9-18).

Эта функция возвращает на место кнопку «Проверить» (строка 11) и отображает сообщение с результатом.

Теперь переходим к коду обработки ajax запроса на стороне сервера. Напомню, что этот код находится в методе ajaxcheck() контроллера.

  1. function ajaxcheck() {
  2.     $this->load->library('validation');
  3.  
  4.     $rules['check'] = "required|callback_captcha_check";
  5.     $this->validation->set_rules($rules);
  6.  
  7.     $fields['check'] = "captcha";
  8.     $this->validation->set_fields($fields);
  9.  
  10.     if ($this->validation->run()) {
  11.         $res['state'] = "OK";
  12.         //тут будет код, обрабатывающий данные формы
  13.     }
  14.     else {
  15.         $res['state'] = "ERR";
  16.         $res['errMessages'][] = $this->validation->check_error;
  17.     }
  18.     echo json_encode($res);
  19.     return;
  20. }

Здесь мы, прежде всего, подключаем библиотеку validation. О ней можно почитать в статье «Проверка данных из форм (CodeIgniter)», поэтому подробно останавливаться на ее работе я не буду.

Главное, обратите внимание, что для проверки полученного значения мы создали правило callback_captcha_check (строка 4), а это значит, что нужно написать метод, который эту проверку будет выполнять.

  1. function captcha_check($str) {
  2.     $exp = time() - 7200;
  3.     $this->db->query("DELETE FROM captcha WHERE captcha_time < ".$exp);
  4.     $qFind = "SELECT COUNT(*) AS count FROM captcha WHERE word = ? AND ip_address = ? AND captcha_time > ?";
  5.     $data = array($this->input->post('check'), $this->input->ip_address(), $exp);
  6.     $query = $this->db->query($qFind, $data);
  7.     $row = $query->row();
  8.  
  9.     if ($row->count == 0) {
  10.         $this->validation->set_message('captcha_check', 'Введенное число не соотвествует, изображенному на рисунке');
  11.         return FALSE;
  12.     }
  13.     else {
  14.         return TRUE;
  15.     }
  16. }

Здесь мы выполняем все операции, приведенные на диаграмме в начале статьи. А именно:
- удаляем из БД все устаревшие записи (строки 2 и 3);
- пробуем найти запись, у которой значения полей ip_address и word совпадают с переданными;
- если такая запись найдена, возвращаем true;
- в противном случае – создаем сообщение с описанием ошибки и возвращаем false.

После выполнения проверки метод ajaxcheck() формирует ответ (строки 11-16) и отправляет его браузеру.

Примечание. Ответ отправляется в формате json. Подробнее о нем можно почитать в статье «Использование JSON в web приложениях».

Кстати, удаление использованных устаревших рисунков выполняется библиотекой автоматически, поэтому можете не бояться, что они займут много места.

Как видите, работа с библиотекой достаточно простая, хотя многие операции приходится выполнять вручную (в смысле писать самостоятельно код).

До встречи!

Понравилась статья? Подписывайтесь на продолжение rss link !

Или на мой твиттер twitter link

]]>

Добавьте эту страницу в google.com bobrdobr.ru del.icio.us technorati.com linkstore.ru news2.ru rumarkz.ru memori.ru moemesto.ru

]]>

Опубликовано в Ajax, CodeIgniter, JavaScript

]]>

Комментарии (38)

Вы можете отслеживать обсуждение записи с помощью RSS 2.0 rss link

Вы также можете оставить комментарий, или трекбек с Вашего сайта.

]]>
  1. MAX  

    Интересно, спасибо.

    Не могу понять только зачем использовать базу данных? Капчу (правильное значение) можно сохранить в сессии или, если попроще, то прямо в форме (hidden) указывать.

    И еще небольшой офтоп. Диаграммы вы в чем делаете? :)

    • Сохранять в сессии или БД, по-моему, без разницы.
      Но из БД удобнее удалять устаревшие картинки и записи.
      А использование hidden может создать проблемы. Достаточно просто написать скрипт, который легко обойдет эту защиту.

      Диаграммы рисую в StarUML.
      Программка неплохая, но есть недостаток - нужно постоянно нажимать F5 иначе вместо кириллицы рисует кракозябры.

      • MAX  

        Спасибо за ссылку. Попробую. Я использую Diagram Designer

        • А Diagram Designer чем-то не устраивает или просто хочется попробовать что-то другое? ;)

      • Nas  

        в сессии не надо ничево удалять… (или я што то не так понимаю..)
        или можно просто $_SESSION['word']==""; помоєму ето проще и удобне..
        у себея сделал именно так.

  2. Мммм… как же приятно видеть диаграммы деятельности в виде алгоритма ;)

    Кстати - в чём наприсовано? У меня RR 2003 - там немного по-другому просто выглядит 8-)

    • Нарисовано в StarUML.
      Программка хорошая, но есть несколько недостатков. В целом, работает быстро, рисовать удобно.

    • Кстати, RR - это Rational Rose?
      Давно хотел попробовать, но все не хватает времени. Один вопрос, UML2 поддерживается полностью? В StarUML - нет.

  3. Вобще неплохо все растолковал…

  4. Netpeak  

    Есть такие капчи, которые создаются вообще без всяких принципов… Вспомнить ту же капчу Made-cat, который щас спамится как сумасшедший. Просто для отвода глаз…

    • Каждый разработчик (владелец) сайта решает этот вопрос сам… он же и расхлебывает последствия.

      Вообще тут большое поле деятельности. Любая капча требует дополнительных действий от посетителя просто для того, чтобы убедится, что он не спамерский бот, а это не очень хорошо (в смысле юзабилити).

  5. Отличная статья. Обязательно добавлю у себя на сайте. Потому что роботы в последнее время проявляют завидную активность :) автору спасибо.

  6. Мне одному кажеться, что блок с кодом нужно сделать побольше?

  7. Tazman  

    Вот бы еще туториал по созданию странички загрузки файлов с использоанием Аякс и CI ;)

    • Wave  

      Да, загрузку файлов аджаксом на CI тоже не прочь был бы увидеть.

      И ещё один то ли совет, то ли просьба по юзабельности сайта.
      Блок: "Вы можете отслеживать обсуждение … или трекбек с Вашего сайта." - визуально от самих комментариев отделите. Хотя бы чертой hr. Будет лучше восприниматься.

    • Вообще-то у меня была похожая идея.
      В CI есть библиотека "upload", с которой довольно легко работать. А загрузка с помощью AJAX или без него отличается только тем, что нужно писать JavaScript функцию.
      В общем, я подумаю что включить в эту статью.

    • Я был неправ. В смысле терминологии. Загрузить файл с помощью AJAX нельзя. Но можно реализовать загрузку так, что посетитель не заметит разницу.

      В общем, следующий пост на эту тему :-) .

  8. Блин, как все не просто. :)Хочу быть кодером.

  9. bukvoed  

    Спасибо за статью - возможно, пригодится в будущем! А пока что я обходился сторонним сервисом captchator.com, нареканий нет.

  10. [...] Добавить проверку CAPTCHA (капча) к форме. [...]

  11. Хорошая статья, а главное полезная.

  12. TYUS  

    Ссылка на пример не рабочая. Поправьте плиз

  13. Dima  

    Мне кажется, что хранить данные captcha в БД - это лишняя нагрузка на сервер.

  14. Radik  

    Заем в БД хранить то просто делаем поле hidden туда помещаем хэш md5 с числом которое написано на картинки потом при вводе просто сравниваем хэши совпал да нет так нет:)

    • Согласен. Честно говоря я уже придумал несколько вариантов улучшения описанного здесь метода (часть решений мне подсказали). Как-нибудь нужно будет отдельный пост написать.

  15. Гость  

    Статья отстой, ничерта не понятно, где скрипты должны лежать откуда эти скрипты скачать, реализация описана только для винды

    • >> Статья отстой, ничерта не понятно

      Это общая фраза. Если нужен конкретный ответ, задайте вопрос.

      >> где скрипты должны лежать

      Контроллер - в папке application/controllers, библиотека - application/libraries, представление - application/views.
      Эту структуру папок определяет фреймворк CodeIgniter.
      Подробнее почитать можно здесь.

      >> реализация описана только для винды

      Винда тут не причем. На линуксе будет работать точно также. Главное чтобы у вас были установлены Apache, PHP и MySQL.

  16. MAD  

    некоторая часть пригодиться :) но в базе хранить это точно не пойдет, ждем продолжения

  17. А не проше взять капче с joomla v1.5 компонент (com_jcommtnt) - там довольно не плохо реализована капче..

    • Может быть и проще, вообще существует много неплохих библиотек для создания captcha. Но здесь я хотел рассказать именно о библиотеке для CodeIgniter.

  18. Evgen  

    Уважаемый Владимир, выложите пожалуйста на любой файлообменник рабочую сборку формы с каптчей, а то очень сложно новичку сообразить что и куда. Буду очень благодарный.

]]>

Оставить комментарий

* - обязательные для заполнения поля

]]>