Регистрация на php ооп
Создание формы регистрации и авторизации на PHP
Внимание! Эта статья только лишь о том, как сверстать форму регистрации и авторизации пользователей на PHP. Сверстать html-форму, а не сделать алгоритм регистрации и авторизации пользователей на PHP, JavaScript и MySQL. Если читателя интересует алгоритм регистрации и авторизации, ему следует изучить материал об этом на сайте моего предприятия. Там читатель найдет решение регистрации и авторизации пользователей на PHP, что называется, «под ключ».
В этой статье мы с вами поговорим о создании формы для авторизации и регистрации пользователей на сайте. Веб формы (html-формы) существуют настолько давно, что многие из на уже и не смогут ответить на вопрос, сколько именно лет пользователи интернета видят их то там, то здесь. Они стали настолько неотъемлемой частью нашей жизни во всемирной паутине, что мысль о том, что форм обратной связи когда-то и не было.
Сейчас же мы поговорим не просто о том, как с помощью упростить для себя «написание» формы, но и создать инструмент для многократного применения, да ещё и не только конкретно в нашем обсуждаемом случае.
Как мы обычно делаем, например, форму обратной связи себе на сайт? Примерно так.
И вот так выглядел бы html-код этой формы авторизации на сайте.
А теперь давайте представим, что пользователь не зарегистрирован, и потому ему перед авторизацией потребуется заполнить данные формы регистрации на нашем сайте. Форма могла бы выглядеть так.
У нас добавились два текстовых поля: одно для повторного ввода пароля, второе для адреса электронной почты пользователя. Наш флажок теперь подпишет пользователя на рассылку, а кнопок стало две. HTML-код такой формы регистрации пользователя мог бы выглядеть так.
Ну, добавилось и добавилось. скажете вы. что такого? Ничего. А если после регистрации вам нужно будет попросить пользователя заполнить форму с подробной информацией о себе? Приведенные примеры форм регистрации и авторизации пользователя не имеют полей для сообщений об ошибках, полей с подсказками. Нет обозначений обязательности заполнений полей.
Привычное для многих использование не даст сразу верный ответ на поставленный вопрос. А ответ прост: написать собственный класс для построения формы обратной связи, формы регистрации и авторизации пользователей и множества других веб-форм.
Если такой класс объявлен в системе, написание вышеупомянутой формы авторизации могло бы выглядеть так.
А давайте теперь посмотрим, насколько «сложнее» будет сделать форму регистрации подобную этой:
Считаю подобный алгоритм лучшим вариантом реализации построения веб-формы (авторизации пользователей, регистрации пользователей и вообще всего того, что связано с формами). Мы один раз написали класс, в котором реализовали сбор информации о полях формы. Этот класс отдает собранные данные, чтобы сформировать из них XML-документ, который будет обработан XSL-шаблоном и выведен на экран.
Подобный алгоритм позволяет нам при написании класса для построения формы не зацикливаться над тем, как построить html-код формы. И что делать, допустим, если мы захотим добавить в форму различные информационные блоки (всплывающие подсказки и другие).
Любые изменения внешнего вида никак не затронут реализацию класса. Все эти изменения можно применить либо изменив указанный XSL-шаблон, либо написав новый XSL-шаблон.
Я не привел код реализации класса. Пока что. Многие из вас поймут, что там даже не один класс, а. несколько. Но, наверное, вы хотели бы лично убедиться в его работоспособности, не так ли? Что будет, если я вам скажу, что даже весь вышеупомянутый код писать нет необходимости? Не верите? Смотрите сами!
Пишем регистрацию на сайте на PHP
Ну вот мы с вами и закончили изучение основных возможностей объектно-ориентированного программирования. Теперь мы будем учиться применять все эти возможности в деле. А именно – напишем движок для блога на чистом PHP. И в сегодняшнем уроке мы узнаем как сделать регистрацию на сайте. Благодаря ей новые пользователи смогут регистрироваться на нашем сайте. Поехали.
В одном из прошлых уроков мы с вами уже создавали таблицу users, которая имеет следующий вид:
Именно её мы и будем использовать для регистрации и авторизации пользователей c помощью PHP и MySQL. Давайте первым делом составим список полей, которые мы должны будем принимать от пользователя для регистрации:
Все остальные поля будут заполняться значениями по-умолчанию на стороне сервера.
Создаём контроллер
Итак, давайте создадим контроллер для работы с пользователями.
Добавляем роут
Теперь давайте пропишем роутинг для данной странички. Пусть это будет myproject.loc/users/register
Проверим, что всё работает:
Создаём шаблон
Отлично, давайте теперь создадим шаблон для нашей новой странички. Шаблон простейший и поэтому кривоватый, вы можете выровнять поля друг под другом с помощью стилей, но это не тема данного курса и здесь фронтенд будет крайне простым, сконцентрируемся на бэкенде.
И теперь отрендерим этот шаблон в нашем контроллере:
Проверяем, что все работает
Пишем логику регистрации
Итак, заготовочку сделали, теперь – самое интересное. Нам нужно обработать данные, пришедшие от пользователя. Принимать данные из запроса мы будем внутри контроллера, однако вся логика по проверке этих данных будет находиться внутри модели пользователя. То есть принять данные из запроса – это всегда ответственность контроллера. Далее его задача – передать эти данные модели, чтобы она произвела с ними какие-то действия(проверила на валидность, сохранила в базу). Контроллеры не должны содержать в себе бизнес-логику (то есть то, как должны обрабатываться и храниться данные). За счет того, что такая логика всегда содержится в моделях, код становится проще реиспользовать (использовать в нескольких местах). Например, мы можем из разных контроллеров обращаться к одному и тому же коду, который хранится в модели. Итак, давайте создадим в модели пользователя статический метод, который будет принимать на вход массив с данными, пришедшими от пользователя, и будет пытаться создать нового пользователя и сохранить его в базе данных.
Именно этот код мы и будем вызывать в контроллере, если пришел POST-запрос.
Проверка входных данных на пустоту
Итак, приступаем к написанию логики в модели. Для начала стоит убедиться в том, что все необходимые данные были переданы в запросе. Если это не так – будем кидать исключение. Для такого рода ошибок заведем отдельное исключение:
Мы будем использовать его для случаев, когда были переданы некорректные параметры или данные.
Итак, пишем проверки на то, что все данные были переданы:
В контроллере теперь нужно научиться обрабатывать эти исключения:
Видите, мы начали передавать в шаблон переменную error? Нужно бы уметь выводить её в шаблоне, если она не пустая:
Теперь попробуем отправить форму регистрации с пустыми полями:
Увидим, что код упал на первой же проверке.
Попробуем заполнить теперь поле nickname и снова отправим запрос.
Как видим, теперь уже ошибка о том, что мы не заполнили email. Отлично, значит наш код действительно проверяет наличие полей. Однако данные, которые мы заполняли перед отправкой формы потеряны, что, согласитесь неудобно, так как придется возвращаться назад. Давайте будем выводить в шаблоне данные, которые были переданы в запросе. Для этого используется атрибут value у тегов input.
Попробуем снова отправить форму с заполненным полем nickname.
Видим всё ту же ошибку, но теперь данные формы не потерялись. А это значит, что наш интерфейс стал гораздо более удобным для пользователя – если возникнет ошибка, ему не нужно будет вводить всё заново.
Проверка данных на валидность
Добавим также проверку на то, что длина пароля не менее восьми символов, а также что nickname содержит только допустимые символы, а email является корректным email-ом:
Поиск дубликатов
Теперь стоит проверить, что пользователя с такими email и nickname нет в базе. Для этого нам понадобится метод, позволяющий находить записи в базе по какому-то одному столбцу. Давайте добавим его в класс ActiveRecordEntity.
Этот метод будет принимать два параметра:
Если ничего не найдено – вернётся null. Если же что-то нашлось – вернётся первая запись.
С помощью этого метода мы сможем искать пользователей по email и nickname:
Тут думаю всё понятно, кроме параметра authToken – это специально случайным образом сгенерированный параметр, с помощью которого пользователь будет авторизовываться. Мы не будем передавать после того как вошли на сайт в cookie ни пароль, ни его хеш. Мы будем использовать только этот токен, который у каждого пользователя будет свой и он никак не будет связан с паролем – так безопаснее.
В конце метода мы сохраняем этого нового пользователя в базу и возвращаем его из метода. Обратите внимание, что у метода появился тип возвращаемого значения: User – не забывайте их указывать.
Теперь в контроллере нужно получать результат этого метода и проверять, что нам вернулся только что созданный пользователь. Если всё ок – писать об этом на форме регистрации.
Пробуем теперь заполнить нашу формочку корректными данными и отправляем запрос.
Заглянем теперь в базу и убедимся, что действительно появился новый пользователь
Вот мы и сделали регистрацию для нашего блога. В следующем уроке мы сделаем активацию пользователя по email-у.
Пишем систему авторизации на PHP
Сегодня мы напишем авторизацию пользователя на сайте. Вся система будет работать следующим образом: пользователь вводит логин и пароль на форме входа, если они правильные – в Cookie браузера будет установлена специальная запись – auth token (авторизационный токен). При дальнейших запросах на сервер этот токен будет проверяться и если он будет правильным, то пользователь считается авторизованным.
Первым делом решаем, что страница с формой логина и пароля будет находиться по адресу http://myproject.loc/users/login. Создаём соответствующий роут.
После этого добавляем новый экшен в контроллере.
И, наконец, создаём шаблон с формой для этого экшена.
Теперь можно зайти в браузер и убедиться, что форма открывается.
Теперь нам нужно добавить обработку отправленной формы и добавить в модели пользователя метод для логина.
Обратите внимание – при успешном входе auth token пользователя в базе обновляется – все его предыдущие сессии станут недействительными.
Проверяем, что ошибки корректно обрабатываются. Для этого пробуем вводить некорректные логин и пароль, а также отправлять форму с пустыми полями.
Отлично, теперь нужно добавить обработку ситуации, когда логин и пароль верны и метод login в модели User вернул нам пользователя.
Создадим специальный сервис, который будет работать с пользовательскими сессиями через Cookie. Назовём его UsersAuthService.
А теперь мы можем использовать его для удобного создания нужной Cookie в контроллере.
Теперь откроем консоль разработчика в Google Chrome и введем правильные логин и пароль. Видим, что нас перекинуло на главную страницу нашего блога, и что была установлена Cookie с именем token.
Теперь нам нужно научиться передавать пользователя во View. Согласитесь, пользователь нам понадобится почти на каждой странице сайта, в каждом экшене. И будет неудобно каждый раз при рендеринге шаблона прокидывать пользователя:
Поэтому мы сделаем во View возможность добавлять переменные еще перед рендерингом, вот так:
И теперь мы можем в контроллерах прямо в конструкторах задать нужные переменные.
И добавить в шапке сайта (в шаблонах) вывод пользователя, если он был передан во View:
Обновим теперь страничку и увидим приветствие.
Если мы сейчас перейдём на страницу со статьёй, то увидим, что система просит нас залогиниться.
Это потому что мы в контроллере статей не прокинули пользователя во View. Давайте добавим тот же код, что и в конструкторе MainController.
Обновим страничку, и увидим, что теперь пользователь был передан в шаблон.
Прежде чем добавлять этот же код в контроллер пользователей давайте подумаем – ведь этот код будет одинаковым во всех трех контроллерах. Так давайте же создадим отдельный абстрактный контроллер, куда поместим этот код, а все наши контроллеры просто от него отнаследуем.
Обратите внимание, свойства user и view теперь с типом protected – они будут доступны в наследниках. Ну а теперь нам достаточно просто отнаследоваться в наших контроллерах от этого класса и можно удалить в них конструкторы и свойства view и user – они будут унаследованы от AbstractController. Это существенно упростит их код.
Теперь можно пройтись по всем страничкам сайта и убедиться, что всё по-прежнему работает. Если теперь нам нужно будет добавить какой-то функционал для всех контроллеров, то мы просто сделаем это в AbstractController.
Ну вот и всё – наша система авторизации готова! Разумеется, есть еще несколько вещей, которые нужно сделать. Их вы реализуете самостоятельно в домашнем задании.
Регистрация и авторизация вместе
Учебник PHP
Практика
Важное
Регулярки
Работа с htaccess
Файлы, папки
Сессии и куки
Работа с БД
Практика по работе с БД в PHP
Перед чтением см. новые уроки раздела «Важное», которые появились выше.
Практика
Движок PHP
Продвинутые БД
Аутентификация
Практика
ООП и MVC
Абстрактные классы и интерфейсы
Трейты
ООП Магия
Практика
Практика: классы как набор методов
Добавляем хеширование паролей
Хранить пароли в открытом виде в базе данных весьма опасно. Злоумышленник может получить доступ к вашей базе и украсть все логины пользователей вместе с их паролями!
Чтобы этого избежать, на пути хакера следует поставить еще одну защиту: хеширование паролей.
Итак, давайте вспомним, как выглядела раньше таблица users:
Добавим теперь вместо паролей их хеши md5():
id | login (Логин) | password (Пароль) |
---|---|---|
1 | user | 827ccb0eea8a706c4c34a16891f84e7b |
2 | admin | 01cfcd4f6b8770febfb40cb906715822 |
Вы можете сами убедиться, глядя на второй вариант таблицы, что хакеру она ничего не скажет!
Теперь нужно доработать код авторизации и регистрации с учетом хеширования. Изменение в коде регистрации будут только тут:
Добавляем соль
На самом деле md5 не дает полной защиты от расшифровки.
В случае простого или очень популярного пароля хеш элементарно расшифровывается с помощью гугла.
Проверьте это сами: пароль для пользователя user был ‘12345’, а его хеш md5 – ‘827ccb0eea8a706c4c34a16891f84e7b’. Вбейте эту строку в гугл и вы сразу получите расшифровку пароля!
Сообщества хакеров часто собираются некоторой группой и запускают генерацию хешей md5 для всевозможных паролей. Для этого они запускают специальные скрипты на своих компьютерах, которые могут работать несколько дней!
Это серьезная проблема для защиты.
Проблема в том, что пользователи имеют тенденцию задавать простые пароли и с этим в принципе ничего не сделать.
Для решения проблемы простых паролей вводится специальная вещь, которая называется соль. Соль представляет собой случайную строку, которую мы добавляем к паролю пользователя. Это называется посолить пароль.
Строка добавляется при регистрации до того, как от пароля взят md5.
Теперь даже если пароль пользователя ‘12345’, то в базе он будет хранится уже соленым и хакер не сможет расшифровать этот пароль ни с помощью гугла, ни с помощью радужных таблиц!
Соль должна представлять собой случайную строку, обычно из 8 символов. Случайный символ получается с помощью конструкции chr(mt_rand(33,126)).
Вот готовая функция для генерации соли:
Соль должна быть уникальная для каждого пользователя.
Соль следует хранить в базе данных, причем в открытом виде, так как она нам понадобится при авторизации.
Пароль, естественно, мы храним в виде хеша (пароль+соль).
Добавим в таблицу users соль (пароли уже посолены md5($password.$salt)):
id | login (Логин) | password (Соленый пароль) | salt (Соль) |
---|---|---|---|
1 | user | 827ccb0eea8a706c4c34a16891f84e7b | sdfLjgyl |
2 | admin | 01cfcd4f6b8770febfb40cb906715822 | sMtrnwpJ |
Различные варианты соления в известных движках:
Изменим код регистрации с учетом соли:
Поменяем теперь код авторизации.
Обратите внимание на то, что если раньше мы проверяли правильность пары логин-пароль с помощью SQL запроса, то теперь так не получится.
Мы должны найти пользователя с заданным логином с помощью SQL запроса (логин из формы авторизации) и далее в скрипте сравнить соленый пароль из базы с паролем из формы (его нужно будет посолить солью из базы):
Валидация при регистрации
При регистрации обязательно следует проверять данные, которые пользователь ввел в поля.
Логин, например, не должен быть меньше трех-четырех символов, а пароль вообще желательно ограничить минимум 6-ю символами (в целях безопасности).
Обратите на это внимание. Я это не показываю, дабы не усложнять примеры, но вам это необходимо будет реализовать в реальных условиях.
Что вам делать дальше:
Приступайте к решению задач по следующей ссылке: задачи к уроку.
Безопасный метод авторизации на PHP
Примечание: мини-статья написана для новичков
Давайте посмотрим вокруг: форумы, интернет магазины, гостевые книги и т.д. используют регистрацию и последующую авторизацию пользователей. Можно даже сказать, что это почти необходимая функция каждого сайта (только если это не домашняя страничка Васи Пупкина или не визитная карточка, какой-нибудь небольшой компании). Сегодня я хочу поделиться со всеми новичками информацией, о том, как лучше это все реализовать.
1. Модель (клиент)
Регистрация
— логин (a-z0-9)
— пароль
Вход
— логин
— пароль
Cookie
— уникальный идентификатор юзера
— хэш
Модель (сервер)
MySQL
Таблица users
user_id (int(11))
user_login (Varchar(30))
user_password (varchar(32))
user_hash (varchar(32))
user_ip (int(10)) по умолчанию 0
При регистрации в базу данных записываеться логин пользователя и пароль(в двойном md5 шифровании)
При авторизация, сравниваеться логин и пароль, если они верны, то генерируеться случайная строка, которая хешируеться и добавляеться в БД в строку user_hash. Также записываеться IP адрес пользователя(но это у нас будет опциональным, так как кто-то сидит через Proxy, а у кого-то IP динамический… тут уже пользователь сам будет выбирать безопасность или удобство). В куки пользователя мы записываем его уникальный индетификатор и сгенерированный hash.
Почему надо хранить в куках хеш случайно сгенерированной строки, а не хеш пароля?
1. Из-за невнимательности программиста, во всей системе могут быть дырки, воспользовавшийсь этими дырками, злоумышленик может вытащить хеш пароля из БД и подставить его в свои куки, тем самым получить доступ к закрытым данным. В нашем же случае, двойной хеш пароля не чем не сможет помочь хакеру, так как расшифровать он его не сможет(теоретически это возможно, но на это он потратит не один месяц, а может быть и год) а воспользоваться этим хешем ему негде, ведь у нас при авторизации свой уникальный хеш прикрепленный к IP пользователя.
2. Если злоумышленик вытащит трояном у пользователя уникальный хеш, воспользовать им он также не сможет(разве если только, пользователь решил принебречь своей безопастностью и выключил привязку к IP при авторизации).
2. Практика
—
— Структура таблицы `users`
CREATE TABLE `users` (
`user_id` int(11) unsigned NOT NULL auto_increment,
`user_login` varchar(30) NOT NULL,
`user_password` varchar(32) NOT NULL,
`user_hash` varchar(32) NOT NULL,
`user_ip` int(10) unsigned NOT NULL default ‘0’,
PRIMARY KEY (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251 AUTO_INCREMENT=1 ;
register.php
// Страница регситрации нового пользователя
$err [] = «Логин может состоять только из букв английского алфавита и цифр» ;
$err [] = «Логин должен быть не меньше 3-х символов и не больше 30» ;
# проверяем, не сущестует ли пользователя с таким именем
$err [] = «Пользователь с таким логином уже существует в базе данных» ;
# Если нет ошибок, то добавляем в БД нового пользователя
# Убераем лишние пробелы и делаем двойное шифрование
header ( «Location: login.php» ); exit();
print «При регистрации произошли следующие ошибки:
» ;
login.php
// Страница авторизации
# Функция для генерации случайной строки
$chars = «abcdefghijklmnopqrstuvwxyzABCDEFGHI JKLMNOPRQSTUVWXYZ0123456789» ;
# Вытаскиваем из БД запись, у которой логин равняеться введенному
# Генерируем случайное число и шифруем его
$hash = md5 ( generateCode ( 10 ));
# Если пользователя выбрал привязку к IP
# Переводим IP в строку
# Записываем в БД новый хеш авторизации и IP
# Переадресовываем браузер на страницу проверки нашего скрипта
header ( «Location: check.php» ); exit();
print «Вы ввели неправильный логин/пароль» ;
check.php
// Скрипт проверки
print «Хм, что-то не получилось» ;
print «Включите куки» ;
Для защиты формы логина от перебора, можно использовать captcha.ru target=»_blank»>капчу.
Хочу отметить, что здесь я рассматривал авторизацию основоную на cookies, не стоит в комментариях кричать, что сессии лучше/удобнее и т.д. Спасибо.