Регистрация и авторизация пользователей на PHP. Часть пятая
У нас, наконец-то, всё готово для непосредственной регистрации пользователя на сайте. Так давайте же попробуем!
Заполняем поля формы данными. Намеренно делаем ошибку в данных: в адресе электропочты вместо точки укажем запятую, а пароль напишем символами кирилицы.
Поле логина подсвечено красной рамкой. Это сделал браузер, так как атрибут type поля input имеет значение email. Адрес электропочты написан с ошибкой, форма не отправится. Позже исправим запятую на точку.
Проходим через тест капчи Google reCAPTCHA.
Отмечаем то, что нас просят выбрать.
Подтверждаем выбор.
Нам остается исправить запятую в адресе электропочты на точку, и отправить данные на регистрацию.
Регистрируем нового пользователя
Не тут-то было. Пароль содержит недопустимые символы. Конечно. Мы же сами написали его кирилицей. Хорошо, исправляем пароль.
Пользователь зарегистрирован. Если бы мы ввели разные пароли в оба поля, была бы ошибка. После регистрации пользователь будет авторизован, если регистрация была успешна. Давайте посмотрим, как это происходит.
Авторизация зарегистрированного пользователя
Сначала в сценарии файла-обработчика AJAX-запросов вызывается метод authUser() для объект модели пользователя.
<?php // Файл service.php // Инициализируем ядро сайта require_once("bootstrap.php"); // Делаем что-либо только если выполняется запрос if (!empty($_REQUEST) && !empty(Core_Array::getRequest("request"))) { // По умолчанию данные передаём в формате JSON $bJson = TRUE; // По умолчанию прерываем работу сценария $bExit = TRUE; // Данные, отправляемые в ответ на запрос по умолчанию $response = array( "success" => FALSE, "message" => "При выполнении запроса что-то пошло не так" ); // Сохраняем строку значения типа запроса $sRequest = strval(Core_Array::getRequest("request")); // Для разных типов запроса разные действия switch ($sRequest) { // Проверка корректности капчи Google reCAPTCHA case "checkCaptcha": // Сохраняем полученное значение капчи $sCaptcha = strval(Core_Array::getRequest("captchaValue")); // Получаем параметры конфигурации для Google reCAPTCHA $aCaptcha_Config = Core_Config::instance()->get("captcha"); // Формируем данные для передачи в запросе $aData = array( "secret" => $aCaptcha_Config["secret_key"], "response" => $sCaptcha ); // Запрос к серверу Google выполним через cURL $curl = curl_init(); // Указываем параметры запроса curl_setopt($curl, CURLOPT_URL, $aCaptcha_Config["url_verify"]); // Нам нужен возврат результата, а не вывод в браузер curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // Будем ли передавать заголовки curl_setopt($curl, CURLOPT_HEADER, FALSE); // Подставляем передаваемые данные curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($aData)); // Выполняем запрос и сохраняем его результат $sResponse = curl_exec($curl); // Преобразуем ответ в массив $response = json_decode($sResponse, TRUE); break; // Проверка уникальности логина пользователя case "checkLoginExists": // Сохраняем переданное значение логина $sEmail = strval(Core_Array::getRequest("email")); // Пробуем найти пользователя по указанному логину $oUser = Core::factory("User") ->getByLogin($sEmail); // Если такого пользователя нет if (is_null($oUser)) { $response["success"] = TRUE; $response["message"] = "Указанный вами логин доступен для регистрации"; } // Если такой логин в системе уже зарегистрирован else { $response["message"] = "Пользователь с указанным логином уже зарегистрирован"; } break; // Регистрация нового пользователя case "userRegistration": // Сохраняем переданные данные $login = strval(Core_Array::getRequest("login")); $password = strval(Core_Array::getRequest("password")); $password_approve = strval(Core_Array::getRequest("password_approve")); // Создаем объект модели пользователя $oUser = Core::factory("User"); // Создаем объект контроллера пользователя $User_Controller = new User_Controller($oUser); // Добавляем в контроллер полученные данные $User_Controller->setLogin($login) ->setPassword($password) ->setPasswordApprove($password_approve); // Сначала проверяем все переданные данные // Если данные не прошли проверку, больше ничего не делаем if (!$User_Controller->checkRegistrationData()) { $response["message"] = "Переданные на регистрацию данные о пользователе не прошли проверку."; break; } // Регистрируем пользователя, сохраняя его в БД $iUserId = $User_Controller->registration(); // Если пользователь успешно сохранен if (!is_null($iUserId)) { // Авторизуем на сайте нового пользователя сразу же $oUser->authUser(); /** * Это может показаться странным, но методу authUser() действительно * не передаётся никакой идентификатор. В объект модели уже были * загружены необходимые данные пользователя в процессе его регистрации */ $response["success"] = TRUE; $response["message"] = "Пользователь с логином " . $oUser->login . " успешно зарегистрирован"; } else { $response["message"] = "Не удалось зарегистрировать пользователя с логином " . $login; } break; } // Если требуется представить данные в формате JSON $bJson && print json_encode($response); // Если требуется прервать выполнение скрипта $bExit && exit(); } else { exit("Доступ к разделу запрещен"); } ?>
А теперь давайте посмотрим, что делает этот метод.
<?php // Файл modules/core/user/model.php defined("LEZH") || exit("Доступ к файлу запрещен"); class User_Model extends Core_ORM { // Запрещенные к загрузке значения полей protected $_forbiddenFields = array( "password" // Пароль пользователя не получаем из БД ); /** * Конструктор класса * Сразу же предусмотрим возможность создания экземпляра класса для конкретного * пользователя, для которого будет передан его идентификатор */ public function __construct($iPrimaryKey = NULL) { // Основные действия будут происходить в конструкторе родительского класса parent::__construct($iPrimaryKey); } /** * Устанавливает авторизованного пользователя */ public function authUser() { // На момент авторизации пользователя уже должен быть известен его идентификатор // Этот идентификатор должен уже быть загружен в данные модели if (!empty($this->id)) { throw new Exception("User_Model::authUser() не установлен идентификатор пользователя"); } // Стартуем сессию, генерируем её новый идентификатор Core_Session::sessionRegenerateId(); // Записываем в сессию идентификатор пользователя $_SESSION["user_id"] = $this->id; // Пользователь авторизован return $this; } /** * Получает авторизованного пользователя */ public function getAuthUser() { } } ?>
Итак, пользователь зарегистрирован, авторизован. Что происходит после его перенравления на главную страницу? Давайте вспомним, что у нас в файле bootstrap.php в корне сайта.
<?php // Файл bootstrap.php // Включаем сессионную опцию use_strict_mode ini_set('session.use_strict_mode', 1); // Инициализируем константу для защиты от стороннего доступа define("LEZH", TRUE); // Определяем каталог исходных файлов нашего сайта define("SITE_DIR", __DIR__); // Определяем путь к каталогу файла с определением ядра сайта define("CORE_DIR", SITE_DIR . DIRECTORY_SEPARATOR . "modules" . DIRECTORY_SEPARATOR . "core" . DIRECTORY_SEPARATOR); // Подключаем файл с определением класса ядра нашего сайта require_once(CORE_DIR . "core.php"); // Определяем путь для подключения внешних файлов для страниц сайта define("INCLUDE_BLOCKS_PATH", SITE_DIR . DIRECTORY_SEPARATOR . "blocks" . DIRECTORY_SEPARATOR); // Инициализируем ядро сайта Core::init(); // Получаем информацию об авторизации пользователя $oCurrentUser = Core::factory("User")->getAuthUser(); // Сохраняем информацию об авторизованном пользователе в хранилище Core_Page::instance()->user = $oCurrentUser; ?>
Метод User_Model::getAuthUser() получает и загружает в объект модели пользователя информацию о нём, если пользователь авторизован. А затем этот объект мы сохраняем в наше глобальное хранилище. Нам осталось лишь написать реализацию этого метода. Обновим код класса User_model
Получение информации об авторизованном пользователе
<?php // Файл modules/core/user/model.php defined("LEZH") || exit("Доступ к файлу запрещен"); class User_Model extends Core_ORM { // Запрещенные к загрузке значения полей protected $_forbiddenFields = array( "password" // Пароль пользователя не получаем из БД ); /** * Конструктор класса * Сразу же предусмотрим возможность создания экземпляра класса для конкретного * пользователя, для которого будет передан его идентификатор */ public function __construct($iPrimaryKey = NULL) { // Основные действия будут происходить в конструкторе родительского класса parent::__construct($iPrimaryKey); } /** * Устанавливает авторизованного пользователя */ public function authUser() { // На момент авторизации пользователя уже должен быть известен его идентификатор // Этот идентификатор должен уже быть загружен в данные модели if (!empty($this->id)) { throw new Exception("User_Model::authUser() не установлен идентификатор пользователя"); } // Стартуем сессию, генерируем её новый идентификатор Core_Session::sessionRegenerateId(); // Записываем в сессию идентификатор пользователя $_SESSION["user_id"] = $this->id; // Пользователь авторизован return $this; } /** * Получает авторизованного пользователя */ public function getAuthUser() { // Запускаем сессию Core_Session::start(); // Получаем идентификатор авторизованного пользователя $iUserId = intval(Core_Array::getSession("user_id")); // Если такой пользователь есть if (!empty($iUserId)) { // Загружаем данные о пользователе в модель $this->find($iUserId); return $this; } return NULL; } } ?>
И теперь после регистрации пользователь будет авторизован, а информация о нем будет загружена в глобальное хранилище в виде ссылки на объект модели. Нам осталось внести небольшие изменения в файл главной страницы, чтобы в этом убедиться.
<?php // Файл index.php require_once('bootstrap.php'); ?> <!doctype html> <html> <head> <title>Регистрация и авторизация пользователей</title> <?php // Подключаем необходимые CSS-файлы require_once(INCLUDE_BLOCKS_PATH . "css.php"); // Подключаем необходимые JS-файлы сценариев require_once(INCLUDE_BLOCKS_PATH . "scripts.php"); ?> </head> <body> <?php // Подключаем блок header для страницы require_once(INCLUDE_BLOCKS_PATH . "header.php"); ?> <main> <div class="container-fluid container-lg"> <h1>Главная страница сайта регистрации и авторизации пользователей</h1> <?php // Если пользователь авторизован if (!is_null(Core_Page::instance()->user)) { // Получаем сохраненный объект модели пользователя $oAuthUser = Core_Page::instance()->user; // Покажем логин print "<p>Вы авторизовались с логином: <strong>" . $oAuthUser->login . "</strong></p>"; // Покажем дату регистрации print "<p>Дата вашей регистрации: <strong>" . (new DateTime($oAuthUser->registration_date))->format("d.m.Y") . "</strong></p>"; // Покажем хэшированный пароль print "<p>Ваш пароль: <strong>" . $oAuthUser->password . "</strong></p>"; } ?> </div> </main> </body> </html>
Проверяем!
А почему на экране не отображается хэш пароля? Всё верно. Ведь это поле относится к запрещенным для чтения.
Теперь очевидно, что содержимое заголовка страницы, где находится форма аутентификации, требует внесения изменений, так как авторизованному пользователю не нужно показывать форму авторизации. И нам нужна кнопка выхода.
Займемся этим в следующей статье.