Капча для сайта с использованием PHP, Bootstrap, jQuery и AJAX

Статья, в которой наглядно рассмотрим процесс создания своей собственной простой CAPTCHA в форме с использованием PHP, Bootstrap, jQuery и AJAX.

CAPTCHA (капча) – это простой тест, предназначенный для того, чтобы отличить человека от компьютера. Т.е. это такой тест, который человек решает легко, а компьютер научить его выполнять крайне сложно.

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

В большинстве случаев CAPTCHA отображается как некоторый искаженный или наложенный на фон текст (код), который посетителю сайта необходимо разобрать и ввести его в некоторое поле. Кроме текста (кода) используется и другие алгоритмы: найти среди множества картинок правильные, собрать пазл, переместить слайдер, нарисовать связь между несколькими картинками и др.

Разрабатывать CAPTCHA (капчу) будем для формы регистрации, расположенной в модальном окне. Данная форма будет состоять из двух полей: логина и email. Создадим форму и модальное окно с использованием классов и компонентов платформы Twitter Bootstrap 3.

Создание модального окна и формы с CAPTCHA


Файл index.html:

<html lang="ru">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Валидация формы с captcha</title>  
  <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" />
</head>
<body>
<!-- Модальное окно -->
<div id="myModal" class="modal fade" tabindex="-1" role="dialog">
  <div class="modal-dialog">
    <div class="modal-content">
      <!-- Заголовок модального окна -->
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal">×</button>
        <h4 class="modal-title" id="myModalLabel">Регистрация</h4>
      </div>
      <!-- Основная часть модального окна, содержащая форму для регистрации -->
      <div class="modal-body">
        <!-- Форма для регистрации -->
  	<form id="myForm" method="post" role="form" name="myForm">
	  <!-- Блок для ввода логина -->
	  <div class="form-group has-feedback">
	    <label for="login" class="control-label">Введите логин:</label>
            <div class="input-group">
	      <span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>         
	      <input type="text" class="form-control" required="required" name="login" pattern="[A-Za-z]{6,}" value="">
	    </div>
	    <span class="glyphicon form-control-feedback"></span>
	  </div>
	  <!-- Блок для ввода email -->
	  <div class="form-group has-feedback">
	    <label for="email" class="control-label">Введите email:</label>
            <div class="input-group">
	      <span class="input-group-addon"><i class="glyphicon glyphicon-envelope"></i></span>
	      <input type="email" class="form-control" required="required" name="email" value="">
	    </div>
	    <span class="glyphicon form-control-feedback"></span>
	  </div>
          <!-- Конец блока для ввода email-->
	  <hr>
          <!--Изображение, содержащее код CAPTCHA-->		  
	  <img id="img-captcha" src="captcha.php">
          <!--Элемент, запрашивающий новый код CAPTCHA-->
	  <div id="reload-captcha" class="btn btn-default"><i class="glyphicon glyphicon-refresh"></i> Обновить</div>
	  <!--Блок для ввода кода CAPTCHA-->
	  <div class="form-group has-feedback">
            <label id="label-captcha" for="captcha" class="control-label">Пожалуйста, введите указанный на изображении код:</label>
	    <input id="text-captcha" name="captcha" type="text" class="form-control" required="required" value="">
	    <span class="glyphicon form-control-feedback"></span>
          </div>
        </form>
      </div>
      <!-- Нижняя часть модального окна -->
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Отмена</button>
        <button id="save" type="button" class="btn btn-primary">Регистрация</button>
      </div>
    </div>
  </div>
</div>

<div class="container">
  <div class="alert alert-success hidden" id="success-alert">
    <h2>Успех</h2>
    <div>Ваши данные были успешно отправлены.</div>
  </div>
  <!-- Кнопка для открытия модального окна -->
  <button id="btn-modal" type="button" class="btn btn-lg btn-success" data-toggle="modal" data-target="#myModal">
    Регистрация
  </button>
</div>

<script src="js/jquery-2.1.4.min.js" type="text/javascript"></script>
<script src="js/bootstrap.min.js" type="text/javascript"></script>
</body>
</html>

Принцип работы создаваемой CAPTCHA изобразим на следующем рисунке:

Принцип работы создаваемой CAPTCHA

Формировать код CAPTCHA будем на сервере с помощью файла captcha.php. В этом файле мы создадим переменную captchastring, которая будет состоять из цифр, строчных и прописных букв латинского алфавита. Создавать случайный код будем посредством перемешивания этих букв и цифр с помощью PHP функции str_shuffle() и последующего извлечения из полученной строки первых 6 символов. Полученная подстрока будет являться CAPTCHA кодом, которую сохраним в эту же переменную (captchastring). После этого будем хранить код CAPTCHA в переменной сессии code.

Чтобы передать CAPTCHA код в форму, его предварительно необходимо защитить от роботов, т.е. сделать так, чтобы его мог прочитать только человек. Для этого возьмем некоторый фон (background.png) и напишем на нём с помощью некоторого шрифта код. Полученное изображение будем использовать для передачи его в форму.

Файл captcha.php:

<?php
//открывает сессию
session_start();

//присваивает PHP переменной captchastring строку символов
$captchastring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz';
//получает первые 6 символов после их перемешивания с помощью функции str_shuffle
$captchastring = substr(str_shuffle($captchastring), 0, 6);
//инициализация переменной сессии с помощью сгенерированной подстроки captchastring,
//содержащей 6 символов
$_SESSION["code"] = $captchastring;

//Генерирует CAPTCHA

//создает новое изображение из файла background.png 
$image = imagecreatefrompng('background.png');
//устанавливает цвет (R-200, G-240, B-240) изображению, хранящемуся в $image
$colour = imagecolorallocate($image, 200, 240, 240);
//присваивает переменной font название шрифта
$font = 'oswald.ttf';
//устанавливает случайное число между -10 и 10 градусов для поворота текста 
$rotate = rand(-10, 10);
//рисует текст на изображении шрифтом TrueType (1 параметр - изображение ($image), 
//2 - размер шрифта (18), 3 - угол поворота текста ($rotate), 
//4, 5 - начальные координаты x и y для текста (18,30), 6 - индекс цвета ($colour),
//7 - путь к файлу шрифта ($font), 8 - текст ($captchastring) 
imagettftext($image, 18, $rotate, 28, 32, $colour, $font, $captchastring);
//будет передавать изображение в формате png
header('Content-type: image/png');
//выводит изображение
imagepng($image);
?>

Проверять CAPTCHA код, который ввёл пользователь, будем в файле verify.php на сервере. Осуществлять это будем посредством сравнения значения хранящейся в переменной сессии code ($_SESSION["code"]) и значения $_POST["captcha"]. В качестве результата сравнения будем возвращать строку true, если данные соответствуют или false, если данные не соответствуют.

Файл verify.php:

<?php
//открывает сессию
session_start();
//проверяет соответствие коду CAPTCHA
if ($_SESSION["code"] == $_POST["captcha"]) {
  //сообщаем строку true, если код соответствует
  echo 'true';
} 
else {
  //сообщаем строку false, если код не соответствует
  echo 'false';
}
?>

Последним этапом разработки формы с CAPTCHA является создание кода JavaScript с применением библиотеки jQuery и AJAX.

Данный код будет выполнять следующее:

  • выводить новый код CAPTCHA при открытии модального окна;
  • выводить новый код CAPTCHA при нажатии на кнопку "Обновить";
  • выполнять валидацию полей формы "Логин" и "Email" с помощью HTML5 функции checkValidity();
  • визуально выделять поля формы, которые прошли и не прошли валидацию;
  • отправлять на проверку код CAPTCHA, который ввёл пользователь на сервер через AJAX в файл verify.php и получать от него результат. Если результат положительный и форма валидна, то код будет скрывать модальное окно и отображать сообщение об успехе. А если результат отрицательный, то код будет выделять поле, содержащее код CAPTCHA, который ввёл пользователь, красным цветом и значком "Ошибка".

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

$(function() {
//выводит новый код CAPTCHA при открытии модального окна  
$('#myModal').on('show.bs.modal', function () {
  $('#img-captcha').attr('src', 'captcha.php?id='+Math.random()+'');
});
//выводит новый код CAPTCHA при нажатии на кнопку Обновить
$("#reload-captcha").click(function() {
  $('#img-captcha').attr('src', 'captcha.php?id='+Math.random()+'');
});  
//при нажатии на кнопку Регистрация (id="save")
$('#save').click(function() {
  //переменная formValid
  var formValid = true;
  //перебирает все элементы управления input, кроме CAPTCHA 
  $('input').each(function() {
    //если текущий элемент CAPTCHA, то пропустить его
    if  ($(this).attr('id') == 'text-captcha') { return true; }
    //найти предков, которые имеют класс .form-group, для установления success/error
    var formGroup = $(this).parents('.form-group');
    //найти glyphicon, который предназначен для показа иконки успеха или ошибки
    var glyphicon = formGroup.find('.form-control-feedback');
    //для валидации данных используем HTML5 функцию checkValidity
    if (this.checkValidity()) {
      //добавить к formGroup класс .has-success, удалить has-error
      formGroup.addClass('has-success').removeClass('has-error');
      //добавить к glyphicon класс glyphicon-ok, удалить glyphicon-remove
      glyphicon.addClass('glyphicon-ok').removeClass('glyphicon-remove');
    } else {
      //добавить к formGroup класс .has-error, удалить .has-success
      formGroup.addClass('has-error').removeClass('has-success');
      //добавить к glyphicon класс glyphicon-remove, удалить glyphicon-ok
      glyphicon.addClass('glyphicon-remove').removeClass('glyphicon-ok');
      //отметить форму как не валидную 
      formValid = false;  
    }
  });
  //проверяем элемент input, в который пользователь вводит код CAPTCHA
  //получаем значение элемента input, содержащего код CAPTCHA
  var captcha = $("#text-captcha").val();
  //если код CAPTCHA пустой, то сразу сообщаем, что он не правильный
  if (captcha=='') {
    inputCaptcha = $("#text-captcha");
    formGroupCaptcha = inputCaptcha.parents('.form-group');
    glyphiconCaptcha = formGroupCaptcha.find('.form-control-feedback');
    formGroupCaptcha.addClass('has-error').removeClass('has-success');
    glyphiconCaptcha.addClass('glyphicon-remove').removeClass('glyphicon-ok');
  }
  //иначе запрашиваем результат у сервера через ajax
  else  { 
    var dataString = 'captcha=' + captcha;
    $.ajax({
      type: "POST",
      url: "verify.php",
      data: dataString,
      success: function(result) {
	inputCaptcha = $("#text-captcha");
        formGroupCaptcha = inputCaptcha.parents('.form-group');
        glyphiconCaptcha = formGroupCaptcha.find('.form-control-feedback');
	//если результат, который вернул сервер, равен true, 
	//то отмечаем, что код валидный и изменяет цвет элементов на зелёный
        if (result==="true") {
          formGroupCaptcha.addClass('has-success').removeClass('has-error');
          glyphiconCaptcha.addClass('glyphicon-ok').removeClass('glyphicon-remove');
          if (formValid) {
            //скрыть модальное окно
            $('#myModal').modal('hide');
            //отобразить сообщение об успехе
            $('#success-alert').removeClass('hidden');
            $('#success-alert').removeClass('hidden');
          }         
        } 
	//иначе отмечает, что код не валидный и изменяет цвет элементов на красный
	else {
          formGroupCaptcha.addClass('has-error').removeClass('has-success');
          glyphiconCaptcha.addClass('glyphicon-remove').removeClass('glyphicon-ok');
        }
      }
    });
  }
});
});

На следующем изображении отображена не валидная форма:

Форма, в которой все поля заполнены пользователем не правильно

На этом изображении отображена форма, у которой все поля, заполненные пользователем, валидны:

Форма, в которой все поля заполнены пользователем правильно



   Bootstrap 0    7160 +1

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

  1. Михаил # 0
    Добрый день! Я новичок в этом деле, но у меня получилось всё сделать и всё работает!!!
    Спасибо за подробный разбор!

    А как реально сделать чтоб по данной форме происходила регистрация нового пользователя?
    Клас пользователей users создал и права им определил!
    И как чтоб информация о данном пользователе из указанной формы с дополнительными полями ввода по собственному усмотрению переносилась в информацию о пользователе уже на сайте???
    1. Александр Мальцев # 0
      В файле verify.php добавить вставку переданных данных (нового пользователя) в базу данных:
      // сведения, необходимые для подключения к базе данных
      $servername = "localhost";
      $username = "username";
      $password = "password";
      $dbname = "nameDatabase";
      // Создаём соединение
      $conn = mysqli_connect($servername, $username, $password, $dbname);
      // Проверяем соединение
      if (!$conn) {
          die("Ошибка соединения: " . mysqli_connect_error());
      }
      // Добавляем данные в таблицу tableName
      var login = $_POST["login"];
      var name = $_POST["name"];
      var email = $_POST["email"];
      $sql = "INSERT INTO tableName(login, name, email)
      VALUES ('$login', '$name', '$email')";
      
      if (mysqli_query($conn, $sql)) {
          echo "Новый пользователь был успешно добавлен";
      } else {
          echo "Ошибка: " . $sql . mysqli_error($conn);
      }
      
      mysqli_close($conn);
      
    2. Иван # 0
      Здравствуйте.
      Подскажите, пожалуйста, что нужно сделать, чтобы капча формировалась только из цифр?
      Меняю строку, например, на $captchastring='1234567890';
      И при этом картинка капчи вообще не выводится. Почему так происходит?
      1. Иван # 0
        Разобрался сам.
      2. Александр # 0
        Здравствуйте, встроил в форму капчу все хорошо работает, но возник один нюанс данные должны отправляться ajax на почту, все это прописано в другом файле php, подключил через require в файле verify.php и получил такую ситуацию, при вводе капчи и нажатии на кнопку отправить форма отправляеться но на frontend пишет что неправильно ввели капчу, если в файле verify.php удалить то подключение то все нормально появляеться, как можно решить данную проблему?

        <?php
        //открывает сессию
        session_start();
        //проверяет соответствие коду CAPTCHA
        if ($_SESSION["code"] == $_POST["captcha"]) {
          //сообщаем строку true, если код соответствует
          echo 'true';
         require 'mail.php';
        } 
        else {
          //сообщаем строку false, если код не соответствует
          echo 'false';
        	
        }
        
        1. Александр Мальцев # 0
          Значит ответ, который Вы получаете в процессе выполнения файла verify.php не соответствует тому, с которым вы сравниваете его в javascript.
          Добавьте в код JavaScript строчку:
          success: function (data) {
            console.log(data); // добавьте эту строчку, чтобы вывести в консоль ответ с сервера
            //...
          }
          
          После этого посмотрите, с чем вы его сравниваете.
          В статье возвращение true или false приведено для примера. Может вы что-то ещё выводите в процессе выполнения php-скрипта…
        2. Михаил # 0
          Здравствуйте. Все перепробовал, не выводится фон капчи с символами… вместо него символическое изображение места картинки и альт капчи. В чем может быть проблема?
          1. Александр Мальцев # 0
            Проверьте есть ли у Вас файлы background.png и oswald.ttf. Эти файлы должны находиться в одном и том же месте (по умолчанию) с файлом captcha.php.
            Второй момент — это необходимо проверить то, что из html файла Вы указываете правильный путь до captcha.php.
            1. Михаил # 0
              Спасибо! первое помогло, действительно captcha.php и файлы картинки в одной папке работают… хотя я и пытался в файле captcha.php указать верный путь до этих файлов.
              1. Александр Мальцев # 0
                Можно и так. Но скорее всего Вы неправильно указывали путь до файлов. В PHP это делается не так, как в JavaScript.
                Ознакомиться с отличием в указание путей в JavaScript и PHP можно в статье «PHP — Корневая директория сайта».

          Вы должны авторизоваться, чтобы оставлять комментарии.