Используем капчу в Laravel

Ромчик
3

laravelДоброго времени суток. В нескольких статьях по Laravel мы рассмотрели регистрацию пользователей на сайте : «Регистрация, активация и авторизация в Laravel (часть 1)», «Регистрация, активация и авторизация в Laravel (часть 2)» и «Регистрация по инвайтам в Laravel». Но ни в одной статье мы не рассмотрели защиту от ботов. Один из самых простых и эффективных способов защиты от ботов – это капча. Думаю, что такое капча объяснять не надо. Давайте исправим данное недоразумение и установим при регистрации пользователя на сайте под управлением Laravel проверку капчи.

Мы будем использовать пакет mews/captcha. На github.

Установка капчи.

Для установки пакета mews/captcha выполним следующую команду:

    composer require mews/captcha

01

Следующим шагом нам необходимо прописать провайдер и алиас для капчи. Для этого открываем файл config/app.php и добавляем следующие строки

    'providers'=>[
         // ...
         Mews\Captcha\CaptchaServiceProvider::class\,
    ];
    'aliases'=>[;
        // ...
        'Captcha'=>Mews\Captcha\Facades\Captcha::class,
     ];

После установки переходим к конфигурированию

Конфигурация капчи

Первое, что нам необходимо сделать – это создать файл конфигурации капчи. Для этого вводим следующую команду:

    artisan vendor:publish

02

В папке config у нас появился файл captcha.php, в котором и находится конфигурация нашей капчи.

В настройках ничего трогать не будем.

Теперь мы можем использовать капчу.

Использование капчи

За основу возьмем уже написанную нами регистрацию в статье «Регистрация по инвайтам в Laravel»

Откроем файл с регистрационной формой resources/views/auth/register.blade.php сейчас в нем следующий код:

<!DOCTYPE html>
<html lang="en">
<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>Регистрация</title>
    <link href="{{ asset('/css/bootstrap.css') }}" rel="stylesheet">
    <script src="{{ asset('/js/jquery.js') }}" type="text/javascript" charset="utf-8"></script>
    <script src="{{ asset('/js/bootstrap.js') }}" type="text/javascript" charset="utf-8"></script>
</head>
<body>
    <div class="container">
        <nav class="navbar" role="navigation">
            <ul class="nav navbar-nav">
                <li class="active">
                    <a href="{{ url('auth/login') }}">Вход</a>
                </li>
            </ul>
        </nav>
        {{--Ошибки --}}
        @if ($errors->has())
            <div class="row">
                <div class="col-md-8 col-md-offset-2">
                    <div class="alert alert-danger" role="alert">
                        <button class="close" aria-label="Close" data-dismiss="alert" type="button">
                            <span aria-hidden="true">×</span>
                        </button>
                        <ul>
                            @foreach($errors->all() as $error)
                                <li> {{{ $error }}} </li>
                            @endforeach
                        </ul>
                    </div>
                </div>
            </div>
        @endif
        <form role="form" method="post" action="{{ url(App::getLocale().'/auth/register') }}">
           {!! csrf_field() !!}
           <div class="form-group">
              <label for="email">Email</label>
              <input type="email" class="form-control" id="email" placeholder="Email" name='email'>
           </div>
           <div class="form-group">
               <label for="invite">Код</label>
               <input type="text" class="form-control" id="invite" placeholder="код" name='invite'>
           </div>
           <div class="form-group">
               <label for="password">Пароль</label>
               <input type="password" class="form-control" id="password" placeholder="Пароль" name="password">
           </div>
           <div class="form-group">
               <label for="confirm_password">Повторите пароль</label>
               <input type="password" class="form-control" id="confirm_password" placeholder="Повторите пароль" name="password_confirmation">
           </div>
           <button type="submit" class="btn btn-default">Отправить</button>
       </form>
   </div>
</body>
</html>

Для того, чтобы вставить ссылку на картинку капчи используем функцию captcha_src(). Изменим наш файл следующим образом:

<!DOCTYPE html>
<html lang="en">
<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>Регистрация</title>
    <link href="{{ asset('/css/bootstrap.css') }}" rel="stylesheet">
    <script src="{{ asset('/js/jquery.js') }}" type="text/javascript" charset="utf-8"></script>
    <script src="{{ asset('/js/bootstrap.js') }}" type="text/javascript" charset="utf-8"></script>
</head>
<body>
    <div class="container">
        <nav class="navbar" role="navigation">
            <ul class="nav navbar-nav">
                <li class="active">
                    <a href="{{ url('auth/login') }}">Вход</a>
                </li>
            </ul>
        </nav>
        {{--Ошибки --}}
        @if ($errors->has())
          <div class="row">
              <div class="col-md-8 col-md-offset-2">
                  <div class="alert alert-danger" role="alert">
                      <button class="close" aria-label="Close" data-dismiss="alert" type="button">
                          <span aria-hidden="true">×</span>
                      </button>
                      <ul>
                          @foreach($errors->all() as $error)
                              <li> {{{ $error }}} </li>
                          @endforeach
                      </ul>
                  </div>
              </div>
          </div>
      @endif
      <form role="form" method="post" action="{{ url(App::getLocale().'/auth/register') }}">
          {!! csrf_field() !!}
          <div class="form-group">
              <label for="email">Email</label>
              <input type="email" class="form-control" id="email" placeholder="Email" name='email'>
          </div>
          <div class="form-group">
              <label for="password">Пароль</label>
              <input type="password" class="form-control" id="password" placeholder="Пароль" name="password">
          </div>
          <div class="form-group">
              <label for="confirm_password">Повторите пароль</label>
              <input type="password" class="form-control" id="confirm_password" placeholder="Повторите пароль" name="password_confirmation">
          </div>
          <div class="form-group">
              <label for=""></label>
              <img src="{{ captcha_src() }}" alt="captcha" class="captcha-img" data-refresh-config="default"><a href="#" id="refresh"><span class="glyphicon glyphicon-refresh"></span></a></p>
          </div>
          <div class="form-group">
              <label>Капча</label>
              <input class="form-control" type="text" name="captcha"/>
          </div>
          <button type="submit" class="btn btn-default">Отправить</button>
       </form>
    </div>
</body>
</html> 

Мы удалили поле для ввода кода (инвайт), и добавили картинку капчи, и поле ввода кода капчи.

Проверим, что у нас получилось:

03

Отлично, теперь нам надо поправить контроллер регистрации. Добавить проверку капчи.

Открываем файл app/Http/Controllers/Auth/AuthController.php

<?php
namespace App\Http\Controllers\Auth;
use App\Code;
use App\Http\Controllers\CodeController;
use App\Http\Controllers\Controller;
use App\Invite;
use App\User;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use Validator;

class AuthController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Registration Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users, as well as the
    | authentication of existing users. By default, this controller uses
    | a simple trait to add these behaviors. Why don't you explore it?
    |
    */
    use AuthenticatesAndRegistersUsers, ThrottlesLogins;
    /**
    * Create a new authentication controller instance.
    *
    * @return void
    */
    public function __construct()
    {
        $this->middleware('guest', ['except' => 'getLogout']);
    }
    /**
    * Get a validator for an incoming registration request.
    *
    * @param array $data
    * @return \Illuminate\Contracts\Validation\Validator
    */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'email' => 'required|email|max:255|unique:users',
            'password' => 'required|confirmed|min:6',
            'invite' => 'required|Invite',
        ]);
    }
    /**
    * Create a new user instance after a valid registration.
    *
    * @param array $data
    * @return User
    */
    protected function create(array $data)
    {
        return User::create([
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }
    public function postRegister(Request $request)
    {
        $validator = $this->validator($request->all());
        if ($validator->fails()) {
            $this->throwValidationException(
                $request, $validator
            );
    }
    $user = $this->create($request->all());
    //создаем код и записываем код
    $code = CodeController::generateCode(8);
    Code::create([
        'user_id' => $user->id,
        'code' => $code,
        'type' =>1,
    ]);
   //Генерируем ссылку и отправляем письмо на указанный адрес
  $url = url('/').'/auth/activate?id='.$user->id.'&code='.$code;
  Mail::send('emails.registration', array('url' => $url), function($message) use ($request)
  {
      $message->to($request->email)->subject('Registration');
  });
  return 'Регистрация прошла успешно, на Ваш email отправлено письмо со ссылкой для активации аккаунта';
}
public function activate(Request $request)
{
    $res = Code::where('user_id',$request->id)
         ->where('code',$request->code)
         ->first();
    if($res) {
        //Удаляем использованный код 
        $res->delete();
        //активируем аккаунт пользователя
        User::find($request->id)
            ->update([
                 'activated'=>1,
        ]);
        //редиректим на страницу авторизации с сообщением об активации
        return redirect()->to('auth/login')->with(['message' => 'ok']);
}
return abort(404);
}
public function postLogin(Request $request)
{
    if (Auth::attempt(['email' => $request->email, 'password' => $request->password, 'activated' => 1])){
        return "Редирект в кабинет";
}

return redirect()->to('auth/login');
}
}

В методе validator уберем проверку invite и добавим проверку капчи:


protected function validator(array $data)

{

return Validator::make($data, [

'email' => 'required|email|max:255|unique:users',

'password' => 'required|confirmed|min:6',

'captcha' => 'required|captcha',

]);

}

Проверим, что у нас получилось. Попробуем зарегистрироваться, введя заведомо неверную капчу.

04

Капча введена неверно – отработало правило валидации капчи. Единственное давайте добавим сообщение об ошибке. Для этого откроем файл resources/lang/ru/validation.php и в массив добавим следующий элемент:

    'captcha' => 'Неверная капча',

Теперь зарегистрируемся:

05

Как помните при регистрации у нас отправлялось письмо для активации аккаунта.

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

Для этого в наш файл с формой регистрации добавим небольшой скрпит:

<script>
    $('#refresh').on('click',function(){
        var captcha = $('img.captcha-img');
        var config = captcha.data('refresh-config');
        $.ajax({
            method: 'GET',
            url: '/get_captcha/' + config,
        }).done(function (response) {
            captcha.prop('src', response);
        });
    });
</script>

Данный скрипт с помощью аякса методом get передает данные по ссылке. За этой ссылкой надо закрепить контроллер и метод которые вернут обновленную капчу. Поехали…

Пропишем роут. Для этого открываем файл app/Http/routes.php и добавляем в него следующий роут:

Route::get('/get_captcha/{config?}', function (\Mews\Captcha\Captcha $captcha, $config = 'default') {
    return $captcha->src($config);
});

Ну вот и все. Давайте проверим. Кликаем по картинке обновления и капча обновляется.

06Вот теперь все.

Понравилась статья? Поделись с друзьями.
  • Add to favorites
  • Добавить ВКонтакте заметку об этой странице
  • Twitter
  • Facebook
  • Мой Мир
  • LiveJournal
  • Одноклассники
  • Блог Я.ру
  • MySpace
  • FriendFeed
  • В закладки Google
  • Google Buzz
  • Яндекс.Закладки
  • Reddit
  • StumbleUpon
  • Technorati
  • del.icio.us
  • БобрДобр
  • LinkedIn
  • Memori.ru
  • Сто закладок
  • Blogger

  • Сергей - 24.12.2015 в 19:41

    Отличные статьи! Пишите больше про Ларавел. Уже вышел релиз по версии 5,2, хотелось бы узнать о ней побольше, в чем отличия, новый функционал и пр.

    • Ромчик - 24.12.2015 в 19:59

      Спасибо. Хотелось бы больше писать, но времени не хватает

  • Алик - 30.09.2018 в 23:11

    Здравствуйте! Спасибо за статью. Проблема с генерацией самой картинки капчи. {!! captcha_img() !!}.
    Вместо картинки квадратик синего цвета.

  • ©2012-2020 По всем вопросам обращайтесь через форму обратной связиПолитика конфиденциальности

    Яндекс.Метрика