Авторизация посетителей на PHP



Рассмотрим систему авторизации пользователей (регистрация, login/logout, доступ к закрытым страничкам, поддтвеждение по e-mail).

Нам понадобятся: вебсервер, PHP, MySQL. Предполагается, что вы с этим всем уже знакомы на определенном уровне.

Для начала создадим в базу данных, допустим, "authorize" и таблицу "users" в ней. Работать с БД MySQL очень удобно с помощью инструмента PhpMyAdmin, который можно скачать здесь.

Итак, таблица "users":

ПолеТипПо умолчаниюДополнительно
idmediumint(9)auto_increment
loginvarchar(32)index unique
passvarchar(32)
emailvarchar(64)index unique
statustinyint(2)0
timestampint(10)

Теперь сделаем страничку регистрации и скрипт, заносящий данные о юзере в базу. Нам понадобится html-файл с такой формой (естественно, если хотите иметь больше информации о юзере, можно добавить соответствующие поля, изменить скрипт и таблицу в БД):

<form action="registration.php" method="post">
<table>
<tr>
   <td>Логин* :</td>
   <td><input type="text" name="rLogin" value="" size="25" maxlength="30" /></td>
</tr>
<tr>
   <td>Пароль* :</td>
   <td><input type="password" name="rPass" value="" size="25" maxlength="30" /></td>
</tr>
<tr>
   <td>Повторите пароль* :</td>
   <td><input type="password" name="rPass2" value="" size="25" maxlength="30" /></td>
</tr>
<tr>
   <td>E-mail* :</td>
   <td><input type="text" name="rEmail" value="" size="25" maxlength="30" /></td>
</tr>
<tr>
   <td></td>
   <td><input type="reset" name="reset" value="Очистить" />
       <input type="submit" name="ok" value="Готово" /></td>
</tr>
</table>
</form>

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

Теперь создадим скрипт "registration.php", который и будет обрабатывать форму регистрации:

<?
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
   $rLogin = trim($_POST['rLogin']);
   $rPass  = trim($_POST['rPass']);
   $rPass2 = trim($_POST['rPass2']);
   $rEmail = trim($_POST['rEmail']);
   if ($rLogin == '') {
      die("Поле 'Логин' не заполнено<br />\n");
   // Логин может состоять из букв, цифр и подчеркивания
   }elseif (!preg_match("/^\w{3,}$/", $rLogin)) {
      die("В поле 'Логин' введены недопустимые символы<br />\n");
   }
   if ($rEmail == '') {
      die("Поле 'E-mail' не заполнено<br />\n");
   // Проверяем e-mail на корректность
   }elseif (!preg_match("/^[a-zA-Z0-9_\.\-]+@([a-zA-Z0-9\-]+\.)+[a-zA-Z]{2,6}$/", $rEmail)) {
      die("Указанный 'E-mail' имеет недопустимый формат<br />\n");
   }
   if ($rPass == '' || $rPass2 == '') {
      die("Поле 'Пароль' не заполнено<br />\n");
   }elseif($rPass !== $rPass2) {
      die("Поля 'Пароль' и 'Повтор пароля' не совпадают<br />\n");
   // Пароль может состоять из букв, цифр и подчеркивания
   }elseif(!preg_match("/^\w{3,}$/", $rPass)) {
      die("В поле 'Пароль' введены недопустимые символы<br />\n");
   }
   // В базе данных у нас будет храниться md5-хеш пароля
   $mdPassword = md5($rPass);
   // А также временная метка (зачем - позже)
   $time = time();
   // Устанавливаем соединение с бд(не забудьте подставить ваши значения сервер-логин-пароль)
   $link = mysql_connect('localhost', $dbuser, $dbpass);
   if (!$link) {
      die("Не могу соединиться с базой данных");
   }else {
      // Выбираем базу данных
      mysql_select_db('authorize', $link);
      // Записываем в базу (не используем addslashes - экранировать нечего)
      mysql_query("INSERT INTO users (login, pass, email, timestamp)
                   VALUES ('$rLogin','$mdPassword','$rEmail',$time)",$link);
      if (mysql_error($link) != "") {
         die("Пользователь с таким логином уже существует, выберите другой<br />\n");
      }
      echo "Юзер добавлен<br />\n";
      mysql_close($link);
   }
}
?>

Не стоит забывать о том, что по Сети лазит множество ботов и от них нужно как-то защищаться, иначе ваша система долго не протянет. Для этого на странице регистрации стоит прицепить какую-нибудь каптчу.

Теперь мы должны выслать юзеру на указанный e-mail письмо со ссылкой для подтверждения регистрации. Для этого вместо строчки echo "Юзер добавлен<br />\n"; вставим такой кусок кода:

// Получаем Id, под которым юзер добавился в базу
$id = mysql_result(mysql_query("SELECT LAST_INSERT_ID()", $link), 0);
// Составляем "keystring" для активации
$key = md5(substr($rEmail, 0 ,2).$id.substr($rLogin, 0 ,2));
$date = date("d.m.Y",$time);
// Компонуем письмо
$title = 'Потвеждение регистрации на сайте Somwhere.net';
$headers  = "Content-type: text/plain; charset=windows-1251\r\n";
$headers .= "From: Администрация Somwhere.net \r\n";
$subject = '=?koi8-r?B?'.base64_encode(convert_cyr_string($title, "w","k")).'?=';
$letter = <<< LTR
   Здравствуйте!
   бла-бла
   
   Ваши регистрационные данные:
      логин: $rLogin
      пароль: $rPass
   
   Для активации аккаунта вам следует пройти по ссылке:
   http://somewhere.net/activation.php?login=$rLogin&key=$key
   
   Данная ссылка будет доступна в течении 5 дней.
   
   $date
LTR;
// Отправляем письмо
if (!mail($rEmail, $subject, $letter, $headers)) {
   // Если письмо не отправилось, удаляем юзера из базы
   mysql_query("DELETE FROM users WHERE login='".$login."' LIMIT 1", $link);
   echo 'Произошла ошибка при отправке письма. Попробуйте зарегистрироваться еще раз.';
}else {
   echo 'Вы успешно зарегистрировались в системе. На указанный вами
   e-mail было отправлено письмо со ссылкой для активации аккаунта.
   У вас 5 дней!';
}

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

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

Теперь у нас в таблице "users" должна быть строка вида:

1   login   76d80224611fc919a5d54f0ff9fba446   eqw@asd.ru   0   1197730343

Теперь создадим скрипт, активирующий пользователей. Система его будет такова:

  • • Пользователь приходит по ссылке с GET-данными "login" и "key"
  • • Берем из базы id, login, e-mail, status и timestamp юзера (если юзер есть). Проверяем статус (активирован уже или нет). Проверяем разницу текущей временной метки и метки из базы, если больше 5*24*60*60 - до свидания
  • • Составляем новую "keystring" по тем же правилам, что и старую. Сравниваем. Не равны - до свидания
  • • Если ключевые строки равны - апдейтим статус до 1.

А код таков:

<?
if (isset($_GET['login']) && isset($_GET['key'])) {
   $login = $_GET['login'];
   $key = $_GET['key'];
   // Делаем проверку login на нехорошие символы
   if (!preg_match("/^\w{3,}$/", $login)) {
      die('Неправильная ссылка!');
   }
   $time = time();
   $link = mysql_connect('localhost',$dbuser,$dbpass);
   if (!$link) {
      die('Не удалось соединиться с БД');
   }else{
      mysql_select_db('authorize', $link);
      $res = mysql_query("SELECT id, email, status, timestamp
      FROM users WHERE login='$login' LIMIT 1", $link);
      // Есть ли пользователь с таким логином?
      if (mysql_num_rows($res) != 1) {
         mysql_close($link);
         die('Такого пользователя нет!');
      }
      $user = mysql_fetch_row($res);
      // Может он уже активен?
      if ($user[2] == 1) {
         mysql_close($link);
         die('Данный логин уже подтвержден!');
      }
      // Успел ли юзер активировать логин? (если нет - удаляем из базы)
      if ($user[3] - $time > 5*24*60*60) {
         mysql_query("DELETE FROM users WHERE login='$login' LIMIT 1", $link);
         mysql_close($link);
         die('Срок активации истёк! Регистрируйтесь заново.');
      }
      $key1 = md5(substr($user[1], 0 ,2).$user[0].substr($login, 0 ,2));
      // Поверяем "keystring"
      if ($key1 != $key) {
         mysql_close($link);
         die('Неправильная контрольная сумма!');
      }
      // Если все проверки пройдены - активируем логин!
      mysql_query("UPDATE users SET status = 1 WHERE login='$login'", $link);
      mysql_close($link);
   }
}
?>

Так, с регистрацией мы покончили, теперь сделаем форму для авторизации, например, такую:

<form method="post" action="login.php">
   <table>
   <tr>
      <td>Логин</td>
      <td><input type="text" name="login"></td>
   </tr>
   <tr>
      <td>Пароль</td>
      <td><input type="password" name="password"></td>
   </tr>
   <tr>
      <td colspan="2"><input type="submit" value="Войти"></td>
   </tr>
   </table>
</form>

Вставляйте эту форму в любое место страницы, как того требует дизайн. Обработчиком формы будет у нас файл "login.php" такого вида:

<?
if (isset($_POST['login'])) {
   $passwordHash = md5($_POST['password']);
   $login = $_POST['login'];
   // Проверка логина на плохие смиволы
   if (!preg_match("/^\w{3,}$/", $login)) {
      die('Неправильный логин!');
   }
   $link = mysql_connect('localhost',$dbuser,$dbpass);
   if (!$link) {
      die('Не удалось соединиться с БД');
   }else{
      mysql_select_db('authorize', $link);
      $res = mysql_query("SELECT status FROM users WHERE login='$login'", $link);
      // Есть ли пользователь с таким логином?
      if (mysql_num_rows($res) < 1) {
         mysql_close($link);
         die('Такого пользователя нет!');
      }
      // Какой статус у пользователя?
      if (mysql_result($res, 0) != 1) {
         mysql_close($link);
         die('Логин не активирован!');
      }
      // Стартуем сессию и записываем логин в суперглобальный массив $_SESSION
      session_start();
      $_SESSION['user'] = $login;
      mysql_close($link);
      // Если определена страница с которой мы пришли,
      // на нее и переадресуем, либо на главную
      if (isset($_SERVER['HTTP_REFERER'])) {
         header ("location: ".$_SERVER['HTTP_REFERER']);
      }else {
         header ("location: index.php");
      }
   }
}
?>

Напишем сразу скрипт "logout.php", работающий как "выход из системы":

<?
   session_start();
   if (isset($_SESSION['user'])) {
   // удаляем элемент "user"
      unset($_SESSION['user']);
   }
   if (isset($_SERVER['HTTP_REFERER'])) {
      header ("location: ".$_SERVER['HTTP_REFERER']);
   }else {
      header ("location: index.php");
   }
?>

Ну вот, юзер авторизован. Осталась малость:

  • • В начале каждого документа стартовать сессию функцией session_start()
  • • Проводить проверку на присутствие в массиве $_SESSION элемента user. На основе нее выдавать "секретный" документ или общий.
  • • Вместо формы для авторизации распечатывать приветствие и ссылку на "logout.php"

Вот простейший пример документа:

<?
session_start();
if (isset($_SESSION['user'])) {
   $auth = "Привет ".$_SESSION['user']."!<br />\n";
   $auth .= "<a href='logout.php'>Выйти</a>";
   $docum = "Эта информация только для зарегистрированных";
}else {
   $auth = <<< AUTH
   <form method="post" action="login.php">
      <table>
      <tr>
         <td>Логин</td>
         <td><input type="text" name="login"></td>
      </tr>
      <tr>
         <td>Пароль</td>
         <td><input type="password" name="password"></td>
      </tr>
      <tr>
         <td colspan="2"><input type="submit" value="Войти"></td>
      </tr>
      </table>
   </form>
AUTH;
   $docum = "Эта общая информация";
}
?>
<html>
<head>
</head>
<body>
<? echo $auth; ?>
<hr />
<? echo $docum; ?>
</body>
</html>

Ну вот вроде и все, о чем я хотел поведать. Надеюсь этот бред кому-нибудь будет полезен :)

UPD: продолжение тут

Комментарии

mysql_query("INSERT INTO users (login, password, email, timestamp)

А должно быть pass

И в logout.php закрывающая скобка лишняя.

а еще там session_start(); не хватает

$mdPassword = md5($password);
Тоже косяк...

Люблю внимательных читателей :)
Исправил в тексте статьи, спасибо.

ОЧЕНЬ полезная вещь. спасибо автору. переделал под sqlite3. ещё раз спасибо!

Добрый день,
спасибо, очень интересная инфа, осбенно для начинающих. Не могли бы Вы кинуть мне в личку almen@inbox.ru. или выложить здесь код в исходниках по названиям файлов. И, если возможно, таблицу в SQL.
Мне не ясно в какой файл я должен поставить фрагмент скрипта, активирующего пользователей.
С уважением Александр

Уважаемый Александр.
Файл активации сохраняеться отдельным файлом activation.php.

Добавляет пользователя в базу даже если он есть уже там. Нет проверки.
mysql_query("INSERT INTO users (login, pass, email, timestamp)
VALUES ('$rLogin','$mdPassword','$rEmail',$time)",$link);
if (mysql_error($link) != "") {
die("Пользователь с таким логином уже существует, выберите другой\n");
}

Эх... уже месяц почти прошел, а в коде чуть ли не бекдор торчит...
Или только мне кажется что когда человек на login.php вводит пароль, его нужно проверять?

И если так оставить то после удачной регистрации пользователь в состоянии обновить раз так 300 страницу регистрации и 300 раз данные запишуться в базу.

Morlok ))))))
а ты попробуй в этом скрипте зарегся и после регистрации иди логиниться, вводи логин тот с которым зарегался а пароль вводи отбалды и вуаля ты тута)))))

Да я уже давным давно этот скрипт до ума довел, но прикольно получилось, хоть выучил PHP [img]http://files.myopera.com/Creat0R/Opera_AC/Icons/Kolobki/smile.gif[/img]

В таблице users, колонка 'дополнительно', строки 'login и 'email' имеют 'index unique'.
Подскажите в какой колонке PhpMyAdmin вводить зти значения?

almen

создал БД "authorize" и таблицу "users" в ней. Сформировал страницы
index.php, registration.php,activation.php, login.php, logout.php
Куда прицепить html файл, заносящий данные о юзере в базу и как его обозвать?

привет,
а я никак не могу этот скрипт у себя отладить, PHP учу с переменным успехом. Был бы безмерно благодарен за исходники в личку almen@inbox.ru

привет Morlok,
а я никак не могу этот скрипт у себя отладить, PHP учу с переменным успехом. Был бы безмерно благодарен за исходники в личку almen@inbox.ru

Хм... дело в том что я этот скрипт уже именил до не узнаваемости в виду собственных нужд. Ну попробую вечером залить более или мение похожее.

Большое спасибо, жду с нетерпением!

Что то вчера не получилось, больно много дел было, надеюсь сегодня вечером выложу.

Я понял,спасибо.

Слушай, а у тебя случайно аськи нет? О.о
Так бы я тебе давно их сбросил и обьяснил что к чему.

Нет, аськи нет. Есть скайп, позывной almen_go
А в чем проблема с мылом?

Долго и нудно обьяснять :)
Я тебя добавлю и все скину.

день добрый,
с основными скриптами разобрался, все заносится в БД, user активируется, но при logout выходит такая ошибка:
Warning: Cannot modify header information - headers already sent by (output started at /users/almen/www/logout.php:28) in /users/almen/www/logout.php on line 130
Вот зтот фрагмент кода if (isset($_SERVER['HTTP_REFERER'])) {
header ("location: ".$_SERVER['HTTP_REFERER']);
}else {
header ("location: index.php");
}
помогите, кто в курсе!

almen, скорее всего у вас что-то выводится в браузер в скрипте logout.php

Этот скрипт должен быть таким, как описано в статье, то есть не выводить никакого html-я. А у вас видно, что в нем строк 130..

убрал лишний код, теперь при logout ошибки нет, но нет и переадресации

3hrek, а пообщаться, например по skype есть возможность? Мой позывной almen_go

Такой возможности, к сожалению нет. Можно по аське 212322504.

сейчас попробую

Отправить комментарий

Содержание этого поля является приватным и не предназначено к показу.
  • Разрешаю теги: <a> <em> <strong> <pre> <ul> <ol> <li>
  • Строки и параграфы переносятся автоматически.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.

Подробнее о форматировании

Image CAPTCHA
Регистр символов учитывается.