Авторизация на PHP через сессии

Ко мне обратился человек, который делал регистрацию и авторизацию для гостевой книги. Он новичок и эта задача вызвала у него большое количество вопросов и проблем. В этом посте я расскажу о том как можно это все делать и как делать не нужно. Все это конечно с примерами.
Итак, есть задача — сделать регистрацию в системе и возможность авторизации. Как это делать? Начнем по порядку.
Регистрация на php
Тут все просто. Делается хранилище данных для пользователей. Обычно это таблица БД. В нее входят такие поля как id, username и password. Остальные поля опциональны. Вы можете собирать e-mail пользователей, их адреса, возможные ip, время выхода в сеть, кодовые слова от банковских карт, секретные вопросы.
В общем, главное — это пара логин-пароль.
Первый важный момент — нельзя хранить пароли пользователей в открытом виде. То есть, как текст. Нельзя потому что если к БД получит доступ кто-то посторонний — он получит базу логин-паролей пользователей, что вряд ли им понравится. А если учесть что логин-пароль у многих пользователей на разных сервисах одни и те же — это ставит под угрозу и личные данные и финансы и личную переписку и все что угодно.
Пароли надо хранить в виде хэша. Хэширование — это операция, которая превращает исходные данные в какой-то слепок известной длины. Это хорошо тем, что слепок уникален. То есть, если мы изменим в исходных данных хотя бы один символ — слепок на выходе операции хэширования получится совершенно, до неузнаваемости другим. Операция хэширования необратима, то есть, из этого слепка не получится развернуть исходные данные. Этим оно и отличается от шифрования.
В MySQL и php есть одна и та же распространенная и безопасная функция хэширования — md5. Этот алгоритм принимает данные и отдает отпечаток.
Итак, мы получаем данные от пользователя, проверяем не пустые ли они, не содержат ли всяких ненужных нам символов (не ограничивайте пользователей слишком сильно). И теперь делаем запрос вроде этого:
INSERT INTO `users` (`id`,`username`,`password`) VALUES('',$quoted_username,MD5($quoted_password));
Обратите внимание — я специально обозвал переменные $quoted_ потому что перед запихиванием их в запрос их обязательно надо отэскейпить функцией mysql_real_escape_string(). Так как эта функция очень часто используется, а пишется очень длинно (люблю архитекторов пхп) — я рекомендую запихать ее в свою оболочку. Например, так:
function quote($var)
Авторизация на php
Мы добавили нового пользователя и теперь он авторизуется. Мы рисуем ему форму логина-пароля и ловим его данные. Что дальше? Ведь пароль нам пришел в открытом виде, а в базе — хэш пароля. Придется конвертировать пароль в хэш, потом сравнивать их? Не, можно сделать проще — в один запрос.
SELECT * FROM `users` WHERE `login`=$qoted_login AND `password`=MD5($quoted_password);
Если запрос вернет строку — это будет строка с данными пользователя. Если нет — такого пользователя в таблице нет. Данные пользователя очень нам пригодятся — стоит их получить в ассоциативный массив.
Запомнить пользователя
Теперь нам надо запомнить что пользователь авторизован и точно знать кто это. Первое что приходит в голову — использовать для этого куки. Действительно — запихать в куки логин и id пользователя и всегда знать кто запрашивает страницу в данный момент.
Но это порочная практика. Почему? Потому что куки — это файл, который хранится у пользователя в браузере и данные из этого файла передаются серверу в каждом запросе. Во-первых, они передаются как текст, а это значит — их легко перехватить. Во-вторых, это простой текст, посылаемый пользователем. Поэтому его можно буквально переписать. Например, если мы решили хранить в куках логин пользователя «Вася» он может открыть управление куками в своем браузере, найти нужную куку и исправить ее на, скажем, «Admin». И все. Теперь при каждом запросе мы будем получать куку, которая нам будет сообщять юзернейм пользователя — «Admin».
Поэтому безопаснее хранить все данные на сервере в недоступном из веба месте. В какой-то папке, к которой нельзя получить доступ из браузера. В эту папку нужно писать все данные о пользователе и читать их оттуда каждый раз, когда он запрашивает документ. Чтобы узнать какому пользователю принадлежит какой файл данных — надо обзывать файл уникальным именем и вот это имя кидать уже пользователю в куки. Таким образом, пользователь не сможет узнать как называется файл для пользователя Admin — этот файл генерирует система на сервере. И это позволяет таскать от документа к документу хоть пароли в открытом виде.
То что я описал — это механизм сессии. В Perl, например, для использования сессий нужно загружать модули. А в php сессии поддерживаются из коробки. Фактически, все что вам нужно знать — это функция session_start() и массив $_SESSION. Это все. Сейчас расскажу.
В каждом скрипте, где вы будете писать в сессию или читать из нее, вам надо до того, как вы выведете какую-то информацию вызвать функцию session_start(). Это запустит сессию. Эта функция создаст файл сессии если его нет или прочитает его если скрипту была передана специальная кука.
Чтобы записать в сессию данные нужно просто записать их в массив $_SESSION. Сейчас нам надо помнить id юзера.
$_SESSION['userid'] = $userinfo['id'];
Все. Теперь каждый раз когда пользователь будет запрашивать скрипт, который использует сессии — вам будет доступно значение элемента $_SESSION[‘userid’].
Поэтому когда пользователь ввел логин и пароль, мы получили его данные из БД, что подтверждает что такой пользователь есть в нашей базе данных, мы запоминаем его в сессии.
Узнать авторизован ли пользователь
Ну это уж проще простого! Теперь, когда вы знаете как работают сессии — узнать авторизован ли пользователь — дело одной строки. Вот она:
if(isset($_SESSION['userid']))
Если в сессии определен id пользователя, значит он уже прошел авторизацию. Но как узнать какой именно это пользователь? Какой его логин? Дата рождения? Кодовое слово для банковской карты? Девичья фамилия матери?
Ну очевидно — сделать один запрос к таблице users. У нас же есть id этого пользователя.
SELECT * FROM `users` WHERE `id`=$quoted_userid
Как разлогинить пользователя, сделать выход
Ну это уж совсем просто. Если мы определяем авторизован ли пользователь по наличию userid в сессии, то чтобы его разлогинить нужно удалить его оттуда. Делается так:
unset($_SESSION['userid']);
Еще можно убить сессию для верности. Это очистит куку у пользователя и уничтожит файл сессии на сервере. При этом из нее пропадут все данные. Делается так:
session_destroy();
На этом все. Если остались какие-то вопросы — не стесняйтесь обращаться. Более того, вы можете обратиться ко мне по icq или почте и попросить помочь с чем-нибудь. Я обычно не отказываю. Если помощь потребуется серьезная — я могу попросить небольшую оплату. Кроме того, я могу дистанционно учить вас делать сайты! Вот такой я коуч и гуру:) А если вы хотите получать уроки и трюки просто так, бесплатно — подпишитесь на RSS моего блога.
Спонсор этого поста — ibooknet.ru, который предлагает ремонт ноутбуков по приятным ценам. Лично мой ноут в порядке и надеюсь, мне не придется его чинить. Чего и вам желаю.
- Домены .рф
- Грабим погоду с Gismeteo.ru
- Получение всех атрибутов тега с помощью регулярных выражений
- Как предотвратить повторную отправку сообщения из формы?
- Работа с кэшированием через API MODx
- Сниппет для MODx импорт RSS
- RSS импорт для MODx версия 2
- Сниппет для MODx граббер погоды с Gismeteo.ru
Как сделать авторизацию на php
Я только начинаю. Подскажите, плиз, как можно сделать авторизацию на PHP? Т.е., чтоб не пользоваться стандартными средствами Апача, а написать собственный скрипт.
————————
p.s. У вас очень хороший форум, много разных вопросов задавал на других форумах и как правило одно и тоже — либо читать man, либо рожей в грязь. У вас такого нет — спасибо большое за это. Кроме того, спасибо за ДЕЙСТВИТЕЛЬНО работающие скрипты — тоже редко где встретишь.
p.s.2 Вопросов у меня было много, но на многие свои вопросы я уже нашел подробные ответы в других топиках форума.
автор: cheops (15.07.2004 в 17:58) письмо автору
Хм давайте начнём с простого примера, а вы скажите достаточно этого или нужно что-то более мощное.
Для начала сделаем HTML-форму и поместим её в файл index.php:
HTML-форма имеет два поля: текстовое поле для имени посетителя, а так же поле типа password для ввода пароля. Кроме того, в форме присутствует кнопка, позволяющая отправить обработчику handler.php данные из формы.
В обработчике handler.php необходимо сверить полученные данные с именем посетителя и его паролем
| // Здесь следует поместить код, например, извлекающий пароль // и имя посетителя из базы данных, и помещающий их в переменные // $password и $name, соответственно. // . // если хеш-коды совпадают, авторизация прошла успешно if ( $_POST [ ‘password’ ] == $password && $_POST [ ‘name’ ] == $name ) echo «Hello, $name » ; else echo «Введен неправильный пароль» ; ?> |
Следующим логическим шагом будет сохранение предварительно зашифрованного пароля в cookie, чтобы посетитель один раз авторизовавшись мог потом некоторое время не вводить пароль. Если хотите, можем рассмотреть и этот вариант.
PS Спасибо на добром слове 🙂 мы как раз и хотели сделать форум, где люди могли бы получить ответ, а не наставления. Меня тоже раздражает стиль новолинуксоидов. ведь сетевое сообщество ещё несколько лет назад было очень дружелюбно к новичкам :(((
автор: грант (15.07.2004 в 18:23)
А как поместить это в базу данных? Если не сложно, подскажите, а то требуют 🙁
автор: cheops (15.07.2004 в 19:20) письмо автору
Это зависит от числа посетителей, для которых создаётся система авторизации. Если посетитель один или несколько штук, то можно вручную создать таблицу и поместить туда несколько записей. Для этого, например, в клиенте mysql необходимов выполнить ряд SQL-запросов. Для создания таблицы user в базе данных test необходимо выполнить SQL-оператор CREATE:
| CREATE TABLE users ( id_user INT NOT NULL auto_increment, name TINYTEXT, password TINYTEXT, PRIMARY KEY (id_user) ) TYPE=MyISAM; |
Теперь таблицу можно заполнить
| INSERT INTO users VALUES(NULL,’user1′,’pass1′); INSERT INTO users VALUES(NULL,’user2′,’pass2′); INSERT INTO users VALUES(NULL,’user3′,’pass3′); |
В результате в таблице users будет 3 записи для посетителей user1, user2, user3 с паролями pass1, pass2, pass3, соответственно. Теперь обработчик формы можно переписать:
| // Имя сервера базы данных, например $dblocation = «mysql28.noweb.ru» // сейчас выставлен сервер локальной машины $dblocation = «localhost» ; // Имя базы данных $dbname = «test» ; // Имя пользователя. Скорее всего будет совпадать с вашим доменным именем // у нас, например $dbuser = «softtime»; $dbuser = «root» ; // Пароль — в комментариях не нуждается 😉 $dbpasswd = «» ; // Соединяемся с сервером базы данных $dbcnx = @ mysql_connect ( $dblocation , $dbuser , $dbpasswd ); if (! $dbcnx ) < echo( « В настоящий момент сервер базы данных не exit(); > // Выбираем базу данных if (! @ mysql_select_db ( $dbname , $dbcnx ) ) < echo( « В настоящий момент база данных не доступна, exit(); > // Формируем и выполняем SQL-запрос для посетителя с // именем $_POST[‘name’] $query = «SELECT password FROM users WHERE name='» . $_POST [ ‘name’ ]. «‘» ; $nme = mysql_query ( $query ); if(! $nme ) < echo «Ошибка выполнения запроса» ; exit(); > // Если запрос вернул результат — производим дальнейшую обработку if( mysql_num_rows ( $nme ) > 0 ) < // Получаем пароль $password = mysql_result ( $nme , 0 ); // Сравниваем пароль из базы данных и введённый посетителем if ( $_POST [ ‘password’ ] == $password ) echo «Hello, $name » ; else < echo «Ошибка идентификации: неправильный пароль» ; exit(); > > // Если в результате запроса не получено ни одной // строки — посетитель с таким именем не зарегистрирован else < echo «Ошибка идентификации: посетитель не зарегистрирован» ; exit(); > ?> |
Если посетителей много, то следует организовать систему регистрации, примерно, как у нас на форуме — тогда база данных будет заполнена самими посетителями.
автор: Shady (07.08.2004 в 13:21) письмо автору
А как к этому скрипту сессии прикрутить?
автор: cheops (07.08.2004 в 13:34) письмо автору
Можно. постараюсь сегодня ближе к вечеру выложить здесь такой скрипт.
автор: cheops (07.08.2004 в 16:36) письмо автору
Вот форма для ввода имени посетителя и пароля (index.php):
| // Это файл index.php session_start (); ?> // Если посетитель «вошёл» — приветствуем его |
Обработчик данной формы должен выглядеть следующим образом (handler.php):
| // Это файл handler.php // Имя сервера базы данных, например $dblocation = «mysql28.noweb.ru» // сейчас выставлен сервер локальной машины $dblocation = «localhost» ; // Имя базы данных $dbname = «test» ; // Имя пользователя. Скорее всего будет совпадать с вашим доменным именем // у нас, например $dbuser = «softtime»; $dbuser = «root» ; // Пароль — в комментариях не нуждается 😉 $dbpasswd = «» ; // Соединяемся с сервером базы данных $dbcnx = @ mysql_connect ( $dblocation , $dbuser , $dbpasswd ); if (! $dbcnx ) < echo( « В настоящий момент сервер базы данных не exit(); > // Выбираем базу данных if (! @ mysql_select_db ( $dbname , $dbcnx ) ) < echo( « В настоящий момент база данных не доступна, exit(); > // Формируем и выполняем SQL-запрос для посетителя с // именем $_POST[‘name’] $query = «SELECT password FROM users WHERE name='» . $_POST [ ‘name’ ]. «‘» ; $nme = mysql_query ( $query ); if(! $nme ) < echo mysql_error (); echo «Ошибка выполнения запроса» ; exit(); > // Если запрос вернул результат — производим дальнейшую обработку if( mysql_num_rows ( $nme ) > 0 ) < // Получаем пароль $password = mysql_result ( $nme , 0 ); // Сравниваем пароль из базы данных и введённый посетителем if ( $_POST [ ‘password’ ] == $password ) < // Идентификация прошла успешно — осуществляем // «вход» посетителя. Для того, чтобы в течении текущей // сесси посетитель не вводил своё имя пароль повторно — // передаём их через сессию if( session_start ()) < $_SESSION [ ‘user’ ] = $_POST [ ‘name’ ]; $_SESSION [ ‘password’ ] = $_POST [ ‘password’ ]; // Осуществляем автоматический переход на // страницу index.php чтобы убедится, что // посетитель «вошёл» echo » » ; |
автор: pavlik (24.09.2004 в 19:39)
не могли бы вы мне помочь.. я сделал авторизацию с сессией, и когда ввожу правильный пароль меня перекидывает снова на страницу авторизации.
и вот самое главное =)
есть база в sql, в ней 5 таблиц, таблица с предметами (предмет и id предмета) , таблица с информацией об учащемся (id учащегося, имя, фамилия и т.д.), таблица с id и pass, успеваемость текущая (код предмета, id ученика, оценка и дата оценки) и успеваемость итоговая ..
теперь мне надо сделать, чтобы пользователь вводил свой пароль и попадал на страничку, где отображалась успеваемость? а успеваемость выводилась из поля с таблицой успеваемости. выводилось определенное поле, сообтветствующее ID пользователя.
подскажите, как это сделать =)
заранее спасибо, павел.
автор: LimP (24.09.2004 в 20:05) письмо автору
вот,я когда-то писал авторизацию,прикрепил архив в аттаче.
автор: pavlik (24.09.2004 в 20:22)
ну это не совсем то, что мне надо =) пользователей никаких нет. у меня успеваемость класса. кол-во учащихся определено и надеюсь не изменится до конца года =) мне просто надо сделать, чтобы выводилась успеваемость учащегося из базы с успеваемостью.. =)
автор: Igorek (24.09.2004 в 20:37) письмо автору
Если я вас правильно понял, то делается это примерно так:
Простенький запрос типа такого: «SELECT mark FROM tableName WHERE AND codPredmeta = $cod». Получаем оценку для определенного юзера и определенного предмета, если нужно для всех предметов, то «AND codPredmeta = $cod» не надо писать.
автор: pavlik (24.09.2004 в 23:38)
не подскажете еще как сделать, чтобы после успешной авторизации человека перебрасывало на страницу с успеваемостью ? =)
автор: glsv (Дизайнер) (24.09.2004 в 23:56) письмо автору
автор: cheops (24.09.2004 в 23:58) письмо автору
В участке кода
| echo »
» ; |
Вместо index.php пропишите адрес страницы с успеваемостью.
автор: eladan (07.11.2004 в 17:32) письмо автору
pri popitke vipolnit’ etot script voznikaet nechto vrode etogo:
Warning: session_start(): Cannot send session cookie — headers already sent by (output started at /home2/p/ptk2001.h12.ru/WWW/login.php:21) in /home2/p/ptk2001.h12.ru/WWW/login.php on line 22
Warning: session_start(): Cannot send session cache limiter — headers already sent (output started at /home2/p/ptk2001.h12.ru/WWW/login.php:21) in /home2/p/ptk2001.h12.ru/WWW/login.php on line 22
s chem bi eto moglo bit’ sviazano?
автор: cheops (07.11.2004 в 18:18) письмо автору
Это значит, что до вызова session_start() имеется вывод в окно браузера, которое заставляет отослать заголовки до того, как интерпретатор доберётся до session_start(). Так как заголовки уже отосланы сессии не инициализируются. Функция session_start() должна быть расположена до любого вывода в окно браузера операторами echo, print и просто HTML-тэгов.
автор: Guest (05.04.2005 в 23:25)
Помогите please! Приведённый пример не работает! Уже устал бться с авторизацией, если не поможете разобью комп чесн. слово
автор: cheops (06.04.2005 в 10:47) письмо автору
Давайте новую тему заведём, а то эта уже очень длинная — здесь просто не удобно разбираться что к чему.
автор: Hemp (08.12.2004 в 10:06) письмо автору
Имеется Windows2000Professional SP4, установлен пакет Денвер-2 (Apache 1.3.27, PHP Version 4.3.6).
______________________________________________
Пытаюсь сделать авторизацию пользователей, используя механизм сессий, следующим образом (на базе найденного мной примера):
Есть, три файла:
1 — reg1.php (выводит форму, для ввода пароля и логина и методом POST передаёт, введённый в поле формы пароль и логин для обработки, скрипту, находящемуся в файле authorize.php )
|
Введите логин и пароль. |