Самописная captcha на PHP для фреймворка Yii2

Польза такого инструмента как captcha трудно пероценить, это и защита от спама, и от несанционированной регистрации ботов, внедрения вредоносных скриптов и многое другое.

Пример captcha Yii2

У Google есть своя встроеная капча, которая встречается во многих сайтах, особенно она популярна в CMS WordPress. У фреймворка Yii2 также есть своя, используемая как widget, которую можно установить и смело пользоваться, — в ней встроено большое колличество дополнительного функционала, но если вам нужна простоя captcha или просто интересно узнать как она работает, тогда в данной теме будет рассмотрен базовый вариант капчи, который будет полезен для верификации при отправке сообщений или обращений пользователей.

Итак, начнем с вида, тоесть с отрисовки самой каптчи в html в форме в папке views, допустим в файле views/layouts/_popUpCall.php:

<script>
$(document).ready(function () {
        $('#CallModal').on('show.bs.modal', function () {
            $('#captcha_image').attr('src', 'call/generate-captcha-ajax?id='+Math.random()+'');
        });
}
function reloadCaptcha() {
        $('#captcha_image').attr('src', 'call/generate-captcha-ajax?id='+Math.random()+'');
    }
</script>
<div class="form-group">
    <div class="row">
        <div class="col-sm-3"></div>
        <div class="col-sm-3" style="margin-bottom: 15px;">
            <img id="captcha_image" src="">
            <button id="captcha_reload" type="button" class="btn btn-info" onclick="reloadCaptcha()"><span class="glyphicon glyphicon-refresh"></span>
            </button>
         </div>
         <div class="col-sm-5">
             <input type="text" class="form-control" id="captcha" name="captcha" placeholder="Введите код из изображения">
         </div>
         <div class="col-sm-1"></div>
     </div>
</div>

В указанном представлении мы осуществляем генерацию капчи при загрузке страницы с помощью jQuery. Затем, в функции reloadCaptcha() можем сделать асинхронную перезагрузку. Ключевой фактор заключается в том, что мы передаем в качестве get параметра строку ‘+Math.random()+», это для того, что бы при новой генерации атрибут src получал новое изображение, иначе браузер не будет просто его менять, так как будет считать его старым ресурсом src. Такая реализация имеется у большинства каптч.

Далее, переходим на бэк-энд и создаем в модели UserModel метод генерации captcha, это основной самый большой метод в данной задаче. В нем, мы рандомно генерируем число и записываем его в сессию:

class UserModel extends Model
{
// ...
public static function generateCaptcha()
    {
        $countChars = 5;
        $countDots = 150;
        $imageHeight = 30;
        $imageWidth = 65;
        $captchaCharsList = '1234567890';
        $captchaString = '';
        for ($i = 1;$i <= $countChars; $i++) {
            $num = random_int(0,strlen($captchaCharsList)-1);
            $captchaString.=$captchaCharsList[$num];
        }
        Yii::$app->session['captcha'] = $captchaString;
        $image = imagecreate($imageWidth,$imageHeight);
        imagecolorallocate($image, 200, 240, 240);
        $textColor = imagecolorallocate($image, random_int(100,255), random_int(0,150), random_int(100,255));
        imagestring($image, 5, 10, 8, $captchaString, $textColor);
        for ($i = 1;$i <= $countDots; $i++) {
            $dotColor = imagecolorallocate($image, random_int(100,255), random_int(0,150), random_int(100,255));
            imagesetpixel($image, rand(1,$imageWidth),rand(1,$imageHeight), $dotColor);
        }
        return $image;
    }
}

Далее, в контроллере CallController мы создаем метод actionGenerateCaptchaAjax():

class CallController extends Controller
{
use yii\web\UnauthorizedHttpException;
// ...
public function actionGenerateCaptchaAjax()
    {
        $this->layout = false;
        Yii::$app->response->headers->add('Content-type','image/png');
        $image = UserModel::generateCaptcha();
        return imagepng($image);
    }
}

Данный метод возвращает как ресурс сгенерированную $image: imagepng($image).

Теперь, перейдем к валидации, тоесть проверке сгенерированного числа и того, что ввел пользователь:

class CallForm extends Model {
// ...
public $captcha;
public function rules()
    {
        return [
            // ...
            ['captcha', 'validateCaptcha'],
        ];
    }
public function validateCaptcha($attributes)
    {
        if (isset(Yii::$app->session['captcha']) &&  $this->captcha == Yii::$app->session['captcha']) {
            return true;
        }
        $this->addError($attributes, 'Неверно указан код изображения!'));
        return false;

    }
}

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

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