Защищенная Авторизация и Регистрация на PHP + MySQL через PDO

Защищенная Авторизация и Регистрация на PHP + MySQL через PDO
PHP скрипты 0    6066 0

В этой статье Вы найдете скрипт на языке программирования PHP, который позволяет пользователю зарегистрироваться и авторизоваться на сайте. Скрипт на 100% защищает от SQL-инжекций, поскольку использует PDO (система подготовленных запросов) и библиотеку RedBeanPHP, которая реализует ORM. Использовать RedBeanPHP мы будем только, чтобы присоединиться к базе данных и легко ей манипулировать, не используя стандартные команды в PHP, такие как mysql_connect и подобные, потому что они устаревшие и не очень эффективные.

Скрипт использует сессии ($_SESSION) и позволяет узнать авторизован сейчас человек или нет. Также в скрипте предусмотрена капча с рандомными вопросами, которая защищает сайт от надоедливого спама.

Скрипт состоит из следующих файлов:

  • index.php - содержит 2 ссылки (на форму авторизации и регистрации);
  • rb.php - ORM-библиотека RedBeanPHP;
  • db.php - подключение к базе данных;
  • login.php - обработчик авторизации пользователя и форма авторизации;
  • signup.php - обработчик регистрации пользователя и форма регистрации;
  • logout.php - выход из сессии.

Как подключить скрипт?

Скачайте готовый скрипт и перенесите все файлы на свой хостинг или локальный сервер (Denwer, OpenServer). Для работы скрипта Вам потребуется версия PHP не ниже 5.6. Далее необходимо создать базу данных и подсоединиться к ней. Для этого потребуется поменять значения в файле db.php. Если с этим возникнут трудности, то Вы можете задавать свои вопросы в комментариях под статьей.


Форма авторизации

В файле login.php находится обработчик и сама форма авторизации, которая состоит из двух полей (логин, пароль). Значок @ (собачка) перед переменными служит в PHP для отключения ошибки, если такая возникнет.

Логин
Пароль

PHP обработчик формы авторизации пользователя

Все переменные, которые возвращаются по методу POST - мы присваиваем переменной $data.

	$data = $_POST;
	if ( isset($data['do_login']) )
	{
		$user = R::findOne('users', 'login = ?', array($data['login']));
		if ( $user )
		{
			//логин существует
			if ( password_verify($data['password'], $user->password) )
			{
				//если пароль совпадает, то нужно авторизовать пользователя
				$_SESSION['logged_user'] = $user;
				echo '
Вы авторизованы!
Можете перейти на главную страницу.

'; }else { $errors[] = 'Неверно введен пароль!'; } }else { $errors[] = 'Пользователь с таким логином не найден!'; } if ( ! empty($errors) ) { //выводим ошибки авторизации echo '
' .array_shift($errors). '

'; } }

Форма регистрации

В файле signup.php находится обработчик и сама форма регистрации. Для таких форм рекомендуется использовать метод запроса POST, при котором веб-сервер принимает данные, заключённые в тело сообщения, для хранения.

Ваш логин
Ваш Email
Ваш пароль
Повторите пароль


PHP обработчик формы регистрации пользователя

	$data = $_POST;

	function captcha_show(){
		$questions = array(
			1 => 'Столица России',
			2 => 'Столица США',
			3 => '2 + 3',
			4 => '15 + 14',
			5 => '45 - 10',
			6 => '33 - 3'
		);
		$num = mt_rand( 1, count($questions) );
		$_SESSION['captcha'] = $num;
		echo $questions[$num];
	}

	//если кликнули на button
	if ( isset($data['do_signup']) )
	{
    // проверка формы на пустоту полей
		$errors = array();
		if ( trim($data['login']) == '' )
		{
			$errors[] = 'Введите логин';
		}

		if ( trim($data['email']) == '' )
		{
			$errors[] = 'Введите Email';
		}

		if ( $data['password'] == '' )
		{
			$errors[] = 'Введите пароль';
		}

		if ( $data['password_2'] != $data['password'] )
		{
			$errors[] = 'Повторный пароль введен не верно!';
		}

		//проверка на существование одинакового логина
		if ( R::count('users', "login = ?", array($data['login'])) > 0)
		{
			$errors[] = 'Пользователь с таким логином уже существует!';
		}
    
    //проверка на существование одинакового email
		if ( R::count('users', "email = ?", array($data['email'])) > 0)
		{
			$errors[] = 'Пользователь с таким Email уже существует!';
		}

		//проверка капчи
		$answers = array(
			1 => 'москва',
			2 => 'вашингтон',
			3 => '5',
			4 => '29',
			5 => '35',
			6 => '30'
		);
		if ( $_SESSION['captcha'] != array_search( mb_strtolower($_POST['captcha']), $answers ) )
		{
			$errors[] = 'Ответ на вопрос указан не верно!';
		}


		if ( empty($errors) )
		{
			//ошибок нет, теперь регистрируем
			$user = R::dispense('users');
			$user->login = $data['login'];
			$user->email = $data['email'];
			$user->password = password_hash($data['password'], PASSWORD_DEFAULT); 
			//пароль нельзя хранить в открытом виде, 
			//мы его шифруем при помощи функции password_hash для php > 5.6
			
			R::store($user);
			echo '
Вы успешно зарегистрированы!

'; }else { echo '
' .array_shift($errors). '

'; } }

Смотреть видеоурок

Статья была написана на основе видеоурока Хауди Хо, который Вы можете посмотреть ниже. Скрипт из видео был немного доработан (добавлена капча function captcha_show).


Похожие статьи:

Парсер на PHP с записью контента в БД
Разрабатываем exploit
Парсер курсов валют на PHP
Удобная форма обратной связи без перезагрузки страницы
Счетчик онлайн посетителей на сайте
Пишем на SQL без SQL: основы по RedBean PHP
Универсальный и очень простой PHP парсер

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

  1. Роман Дубровский 11 февраля 2018, 19:03 # +1
    Простите, не очень понял. Какой код должен быть на странице с закрытым контентом?

    Уже понял. Содержание страницы надо расположить между <?php if… и <?php else: ?>
    1. Алексей Власов 24 мая 2018, 23:48 # 0
      Спасибо за комментарий! Всё верно.
    2. Виталий Алехин 29 марта 2018, 19:38 # +1
      1)<strong><!--?php captcha_show(); ?--></strong> <input type="text" name="captcha"> — ошибка в описании статьи: комментарии здесь не нужны
      2)$errors[] = 'Ответ на вопрос указан не верно!'; var_dump($answers); — опять же непонятно для чего тут в статье и в файле нужен
      var_dump($answers);
      С уважением,
      Виталий
      1. Алексей Власов 24 мая 2018, 23:54 # 0
        Спасибо за помощь!

        var_dump действительно не нужен, забыл удалить его после тестов. В архиве и статье исправил.

        «ошибка в описании статьи: комментарии здесь не нужны» — комментарии создал плагин, убрать их статьи не получится, так как там симбиоз html и php кода. Люди, которые это читают, — смело качайте архив, там нет ничего лишнего. Всё исправно работает!
      2. Victor Sproot 06 июля 2018, 21:51 # 0
        скрипт хороший спасибо
        вот только как защитить его от перебора паролей, а самое главное как сделать чтоб на страницу которую входишь после авторизации можно бы было попасть только авторизованным а то иначе если ввесли в строке адрес то можно без труда туда попасть
        1. Victor Sproot 06 июля 2018, 22:39 # 0
          ну с тем чтоб не открывалось для неавторизованных я разобрался тем что впихнул всю страницу в if
          а вот как с перебором бороться?
          1. Алексей Власов 12 июля 2018, 10:33 # 0
            Спасибо за комментарий! Можно писать в сессию число попыток и если перевалит за какое-то число, то выводить рекапчу гугловскую, например или просто блокировать.
          2. Kill Mania 14 сентября 2018, 10:29 # 0
            Здравствуйте.

            Воспользовался Вашим решением. Все работает отлично. Спасибо большое!

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

            Подскажите, пожалуйста, если не сложно.

            Спасибо.
            1. Алексей Власов 14 сентября 2018, 13:06 # 0
              Спасибо за комментарий!

              Содержание страницы надо расположить между конструкциями <?php if ( isset ($_SESSION['logged_user']) ): и <?php else: ?>. Это уже реализовано в файле index.php, можно там подглядеть. Если человек не авторизован, то увидит надпись «Вы не авторизованы». А если он авторизовался, то увидит надпись «Авторизован!». По аналогии можно поступить со своим контентом.

              А чтобы определить авторизован человек или нет, — надо воспользоваться сессиями. session_start(); — стартует новую сессию, либо возобновляет существующую. В файле db.php это также реализовано.

              Надеюсь, что помог, а не ещё больше запутал.
              1. Kill Mania 15 сентября 2018, 08:58 # 0
                Спасибо большое, Алексей!)

                Нет, вроде бы Вы меня не запутали. Но… Когда я экспериментирую со страничкой index.php, то все получается отлично. Тестовый текст отображается только тогда, когда я авторизован.

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

                Авторизован! Привет, login; ?>! Выйтии

                Вы не авторизованы Авторизация РегистрацияУверен, что где-то допускаю глупую ошибку, скорее всего связанную с недостаточным пониманием логики работы кода. Пишу так:

                <?php require 'db.php'; ?> <?php if ( isset ($_SESSION['logged_user']) ) : ?> Авторизован! Привет, <?php echo $_SESSION['logged_user']->login; ?>! <a href="logout.php">Выйти</a> <!DOCTYPE html> <html lang="en"> <head> </head> <body> И тут внутренности страницыВнизу страницы:

                </body> </html> <?php else : ?> Вы не авторизованы <a href="/login.php">Авторизация</a> <a href="/signup.php">Регистрация</a> <?php endif; ?>Вроде бы все так делаю на мой взгляд…
                1. Kill Mania 15 сентября 2018, 09:16 # 0
                  В общем, после некоторых изучений сломал себе мозг полностью… Две одинаковых страницы: index.html и index.php На index.php все работает, на index.html не работает Не понимаю…
                  1. Kill Mania 15 сентября 2018, 09:33 # 0
                    Переименовал свою страницу index.html в index.php И даже все заработало. Но появилась ошибка: Warning: session_start(): Cannot send session cache limiter - headers already sent (output started at C:\OSPanel\domains\localhost\index.php:1) in C:\OSPanel\domains\localhost\db.php on line 10Если посмотреть на db.php, то там это session_start();, а в index.php это <?php require 'db.php'; ?>
                    1. Kill Mania 15 сентября 2018, 09:45 # 0
                      И тут победил — проблема была в кодировке. Поставил без BOM и все заработало! Уффф… как оно все сложно, мозг кипел с самого утра)))

                      Алексей, спасибо Вам огромнейшее еще раз! Вы заставили мой мозг поработать)) И, конечно же, помогли в решении моей задачи. Особенно фраза «Это уже реализовано в файле index.php, можно там подглядеть» для меня стала решающей, так как начал с ней экспериментировать и все пошло в нужном направлении.

                      От всей души СПАСИБО!!!
                      1. Алексей Власов 15 сентября 2018, 12:01 # +1
                        Очень здорово, что всё получилось! Я рад, что смог помочь :) Спасибо за вопросы. Возможно, они ещё кого-то выручат.
            2. Дионис Дуров 14 сентября 2018, 17:33 # 0
              Добрый день! Вот появился вопрос. Данные для бд ввёл. А как нужную таблицу обозначить?
              P.S. Я вообще тёмный лес в этом вопросе.
              1. Дионис Дуров 14 сентября 2018, 17:49 # 0
                И можно ли просто файл импорта таблицы чистый ещё?
              2. Алексей Власов 14 сентября 2018, 20:25 # 0
                Спасибо за комментарий!

                В данном скрипте для работы с базой данных используется библиотека RedBean PHP. По этой ссылке можно подробнее про неё почитать и посмотреть примеры.

                Например, тут $user = R::dispense('users'); используется метод dispense, в который передается название таблицы users.
                1. Дионис Дуров 14 сентября 2018, 21:05 # 0
                  Конечно, спасибо за ответ, но какая структура таблицы нужна?
                  1. Алексей Власов 14 сентября 2018, 21:55 # 0
                    Можно таблицу и структуру вообще не создавать. Достаточно придумать для таблицы имя, а RedBean PHP уже сам создаст таблицу в БД с правильной структурой
                    1. Алексей Власов 14 сентября 2018, 22:09 # 0
                      Вот тут можно скачать дамп базы, там уже есть структура готовая
                    2. Дионис Дуров 14 сентября 2018, 21:12 # 0
                      т.е. код в файле db.php такой?
                      <?php
                      require 'libs/rb.php';
                      R::setup( 'mysql:host=127.0.0.1;dbname=homework54','myuser, 'mypass' );
                      $user = R::dispense('users');
                      if ( !R::testconnection() )
                      {
                      exit ('Нет соединения с базой данных');
                      }

                      session_start();
                      ?>
                      с учётом, того, что название таблицы: «users»
                      1. Алексей Власов 14 сентября 2018, 22:00 # 0
                        db.php трогать не нужно, там просто подключение к БД. Именно к таблице обращаются, когда нужно сделать конкретное действие, например, записать туда данные или изменить их
                        1. Дионис Дуров 14 сентября 2018, 22:06 # 0
                          Алексей, если честно, ничего не понял) Тогда где таблицу указывать?
                          1. Алексей Власов 14 сентября 2018, 22:13 # 0
                            А для чего её указывать?) Достаточно просто подключиться к своей базе данных и даже, если она будет пустая, то скрипт уже сам все сделает. В файле signup.php уже указывается таблица на 76 строчке
                          2. Дионис Дуров 14 сентября 2018, 22:12 # 0
                            т.е. db.php должен быть такой?
                            <?php
                            require 'libs/rb.php';
                            R::setup( 'mysql:host=127.0.0.1;dbname=redbeen','root', '' );

                            if ( !R::testconnection() )
                            {
                            exit ('Нет соединения с базой данных');
                            }

                            session_start();
                            ?>

                            Тогда где параметры подключения указывать?
                            1. Алексей Власов 14 сентября 2018, 22:15 # 0
                              Да, нужно только свои данные ввести от БД

                              R::setup( 'mysql:host=127.0.0.1;dbname=имя_вашей_базы','ваш_логин_от_бд', 'ваш_пароль_от_бд' );
                      2. Дионис Дуров 17 сентября 2018, 14:14 # 0
                        Снова здравствуйте!
                        Вот в $_SESSION['logged_user'] я с горем пополам сделал чтобы заносилось только это

                        array(3) { [«auth»]=> string(5) «admin» [«captcha»]=> int(1) [«logged_user»]=> string(5) «admin» }

                        Вместо громадного массива. Теперь есть вопрос. Как достать оттуда значение «logged_user» или «auth» (они в моём случае равны admin)
                        1. Дионис Дуров 17 сентября 2018, 14:18 # 0
                          Мне нужно, чтобы заработал этот скрипт

                          <?php
                          ini_set('error_reporting', E_ALL);
                          ini_set('display_errors', 1);
                          ini_set('display_startup_errors', 1);
                          @session_start();
                          include 'config.php';
                          $login = $_SESSION[«logged_user»];
                          var_dump( $_SESSION);
                          $link = mysqli_connect(HOST, USERNAME, PASS, DBNAME);
                          mysqli_set_charset($link, «utf8mb4_unicode_ci»);
                          $login = strval($login);
                          $res=mysqli_query($link,«SELECT * FROM `users` WHERE `login` = '$login' „);
                          $row = mysqli_fetch_array($res);
                          echo $row;
                          if (!$row['usertype']) echo “Тут можно вывести форму входа»;
                          else if ($row['usertype'] === 2) echo «Admin-доступ»;
                          else if ($row['usertype'] === 1) echo «Вы модератор»;
                          else if ($row['usertype'] === 0) echo «Вы обычный пользователь»;
                          ?>
                          1. Дионис Дуров 17 сентября 2018, 15:03 # 0
                            Пока на выходе даёт

                            array(3) { [«auth»]=> string(5) «admin» [«captcha»]=> int(1) [«logged_user»]=> string(5) «admin» }
                            Notice: Array to string conversion in /var/www/user290/data/www/engineer-school.space/test.php on line 14
                            ArrayТут можно вывести форму входа

                            вот скрин
                        2. Сергей Медведев 19 октября 2018, 09:18 # 0
                          Добрый день!
                          Реализовал форму регистрации с шифрованием немного отличным от того что преподносится в уроке не стандартным php хешем, a sha1, теперь хотелось бы прикрутить функцию восстановления пароля к примеру Нажимаем восстановить пароль и данные отсылаются на почту указанную в при регистрации. Но что то не особо получается. Подскажите пожалуйста как правильно подцепить к своей форме данную возможность.
                          Спасибо.
                          1. Алексей Власов 19 октября 2018, 14:17 # 0
                            Сергей, добрый день!

                            Вас интересует алгоритм действий? Я бы ничего восстанавливать не стал, а создал новый рандомный пароль и отправил его на почту:

                            1. делаем форму восстановления
                            2. вводим в нее почту, на которую регистрировались
                            3. проверяем есть ли такая почта в базе
                            3.1. если нет, то предлагаем создать новый аккаунт
                            3.2. если есть, то генерируем пароль из N рандомных символов. Кодируем их, как хотим и сохраняем в базу для этой почты
                            4. шлем письмо с новым паролем

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

                          Вы можете авторизоваться на сайте через:
                          YandexVkontakte

                          Рубрики блога

                          Последние комментарии

                          Алексей Власов 16 октября 2018, 14:27
                          Универсальный и очень простой PHP парсер 10
                          Алексей Власов 13 октября 2018, 18:57
                          Бесконечное сохранение ресурсов в MODX 2
                          Александр Петров 30 сентября 2018, 20:54
                          Пишем на SQL без SQL: основы по RedBean PHP 10