Управление сеансами в РНР.
Что такое управление сеансом.HTTP иногда называют "протоколом без состояния". Это означает, что данный протокол не имеет встроенного способа поддержки состояния между двумя транзакциями. Когда пользователь запрашивает друг за другом две страницы, HTTP не обеспечивает возможности уведомить, что оба запроса исходят от одного и того же пользователя. Таким образом, идея управления сеансами заключается в обеспечении отслеживания пользователя в течение одного сеанса связи с Web-сайтом. Если это удастся осуществить, мы сможем легко поддерживать подключение пользователя и предоставление ему содержимого сайта в соответствии с его уровнем прав доступа или персональными настройками. Мы сумеем отслеживать поведение пользователя.
В более ранних версиях РНР управление сеансами осуществлялось средствами PHPLib, базовой библиотеки РНР, которая и сейчас является полезным набором инструментов. Об этом можно прочесть на
http://phplib.netuse.de/index.php3
Четвертая версия РНР включает собственные встроенные функции управления сеансом. Концептуально они подобны PHPLib, но PHPLib помимо этого обеспечивает em и ряд дополнительных функциональных возможностей. Так что если окажется, что эти собственные функции не вполне отвечают вашим требованиям, ничто не мешает рассмотреть возможность использования PHPLib.
Основные функциональные средства управления сеансом.Для запуска сеанса в РНР используется уникальный идентификатор сеанса, представляющий собой зашифрованное случайное число. Идентификатор сеанса генерируется РНР и сохраняется на стороне клиента в течение всего времени жизни сеанса. Для хранения идентификатора сеанса используется либо cookie-набор на компьютере пользователя, либо URL.
Идентификатор сеанса играет роль ключа, обеспечивающего возможность регистрации некоторых специфических переменных в качестве так называемых переменных сеанса. Содержимое этих переменных сохраняется на сервере. Единственной информацией, "видимой" на стороне клиента, является идентификатор сеанса. Если во время определенного подключения к вашему сайту идентификатор сеанса является "видимым" либо в cookie-наборе, либо в URL, имеется возможность получить доступ к переменным сеанса, которые сохранены на сервере для данного сеанса. По умолчанию переменные сеанса хранятся в двумерных файлах на сервере (при желании способ хранения можно изменить и использовать вместо двумерного файла базу данных, но для этого потребуется написать собственную функцию).
Скорее всего, придется иметь дело с Web-сайтами, на которых для хранения идентификатора сеанса используется URL. Если в вашем URL имеется строка данных, которые выглядят случайными, то это, скорее всего, свидетельствует об использовании одной из двух описанных здесь разновидностей управления сеансом.
Другим решением проблемы сохранения состояния на протяжении некоторого количества транзакций, при наличии чистого внешнего вида URL, являются cookie-наборы.
Что такое cookie-набор?cookie-набор — это небольшой фрагмент информации, который сценарии сохраняют на клиентской машине. Чтобы установить cookie-набор на машине пользователя, необходимо отправить ему HTTP-заголовок, содержащий данные в следующем формате.
Set-Cookie: NAME=VALUE; [expires=DATE;] [path=PATH;] [domain=DOMAIN_NAME;] [secure]
Это создаст cookie-набор с именем NAME и значением VALUE. Все остальные параметры являются необязательными. В expires задается дата истечения срока действия, после наступления которой cookie-набор перестанет рассматриваться как актуальный. Заметим, что если дата истечения срока действия не задана, cookie-набор будет постоянным, пока его кто-нибудь не удалит вручную — либо вы, либо сам пользователь). Два параметра path и domain применяются для определения одного или нескольких URL, к которым относится данный cookie-набор. Ключевое слово secure означает, что cookie-набор не может отправляться через простое HTTP-соединение.
Когда браузер соединяется с URL, он сначала ищет cookie-наборы, хранящиеся локально. Если какие-либо из них относятся к URL, с которым установлено соединение, они передаются обратно на сервер.
Установка cookie-наборов из РНР.cookie-наборы в РНР можно установить вручную, используя функцию setcookie(). Она имеет следующий прототип:
int setcookie (string name [, string value [, int expire [, string path [, string domain [, int secure]]]]])
Параметры в точности соответствуют тем, которые используются в описанном выше заголовке Set-Cookie. Если cookie-набор установлен как
setcookie ("mycookie", "value");
то когда пользователь обращается к следующей странице на вашем сайте (или перезагружает текущую страницу), вы получаете доступ к переменной с именем $mycookie, которая содержит значение "value". Доступ к этой переменной можно получить также через $HTTP_COOKIE_VARS["mycookie"].
Для удаления cookie-набора необходимо вызвать setcookie() с тем же именем, но без указания значения. Если cookie-набор устанавливался с другими параметрами (такими как специфические URL или даты истечения), потребуется отправить те же параметры повторно, иначе cookie-набор удален не будет.
Для установки cookie-набора вручную можно воспользоваться также функцией Header() и описанным выше синтаксисом представления cookie-набора. Однако при этом следует иметь в виду, что заголовки cookie-наборов должны отправляться перед всеми другими заголовками (иначе заголовки cookie-наборов работать не будут).
Использование cookie-наборов в сеансах.При использовании cookie-наборов возникают некоторые проблемы: есть браузеры, которые не принимают cookie-наборы, а есть пользователи, которые запрещают использование cookie-наборов в своих браузерах. Это одна из причин, по которым в сеансах РНР используются двойной метод cookie-набор/адрес URL (ниже этот вопрос рассматривается более подробно).
В сеансе РНР нет необходимости задавать cookie-наборы вручную. Это за вас сделают функции сеанса. Для того чтобы просмотреть содержимое cookie-набора, установленное при управлении сеансом, можно воспользоваться функцией session_get_cookie_params(). Она возвращает ассоциативный массив, содержащий элементы lifetime, path и domain.
Можно использовать также:
session_set_cookie_params($lifetime, $path, $domain);
Этот оператор устанавливает параметры cookie-набора для сеанса.
Если возникнет желание получить более подробную информацию о cookie-наборах, то за консультациями по спецификации cookie-наборов следует обратиться на сайт компании Netscape:
http://home.netscape.com/newsref/std/cookie_spec.html
Сохранение идентификатора сеанса.В РНР cookie-наборы в сеансах используются по умолчанию. Если есть возможность установить cookie-наборы, то для сохранения идентификатора сеанса будет использоваться именно этот способ. Другой метод, который может применяться в РНР, заключается в добавлении идентификатора сеанса к адресу URL. Можно сделать так, чтобы идентификатор сеанса добавлялся к URL автоматически — для этого следует скомпилировать РНР с опцией --enable-trans-sid.
Можно встроить идентификатор сеанса в ссылку, чтобы обеспечить его передачу. Идентификатор сеанса будет запоминаться в константе SID. Для того, чтобы передать его вручную, его потребуется добавить в конец ссылки, аналогично параметру GET:
<А HREF="link.php?<?=SID?>">
В общем случае проще компилировать РНР с --enable-trans-sid, если только это возможно (заметим попутно, что константа SID может использоваться для вышеописанных целей только в том случае, если конфигурация РНР выполнялась с --епаblе-track-vars).
Реализация управления простым сеансом.Основными этапами использования сеанса являются следующие:
· Запуск сеанса
· Регистрация переменных сеанса
· Использование переменных сеанса
· Отмена регистрации переменных и закрытие сеанса
Заметим, что все перечисленные этапы не обязательно могут содержаться в одном сценарии, и некоторые из них могут находиться в нескольких сценариях. Рассмотрим каждый из этих этапов последовательно.
Запуск сеанса.Прежде чем можно будет воспользоваться функциональными возможностями сеанса, следует запустить сам сеанс. Существует три способа сделать это.
Первый (и самый простой) заключается в том, что сценарий начинается с вызова функции
session_start() ;
Эта функция проверяет, существует ли идентификатор текущего сеанса. Если нет, она его создает. Если же идентификатор текущего сеанса уже существует, она загружает зарегистрированные переменные сеанса, чтобы они стали доступными для использования.
Надо отметить, что это прекрасный способ — вызов session_start() в начале сценариев, в которых используются сеансы.
Второй способ заключается в том, что сеанс запускается при попытке зарегистрировать переменные сеанса (см. далее).
Третий способ запустить сеанс — задать установки РНР, при которых сеанс будет запускаться автоматически, как только кто-либо посетит ваш сайт. Для этого следует воспользоваться опцией session.auto_start в файле php.ini (более подробно указанный способ будет описан при рассмотрении конфигурации).
Регистрация переменных сеанса.Для того чтобы получить возможность отслеживать переменные от одного сценария другому, их необходимо зарегистрировать. Это делается путем вызова функции session_register(). Например, для регистрации переменной $myvar применяется следующий код.
$myvar = 5; session_register("myvar");
Обратите внимание: вы должны передать в функцию session_register() строку, содержащую имя переменной. Эта строка не должна включать символ $. Данный оператор регистрирует имя переменной и отслеживает ее значение. Отслеживание переменной будет осуществляться, пока не завершится сеанс либо пока вручную не отменится ее регистрация.
За один прием можно зарегистрировать более одной переменной, передав разделенный запятыми список имен переменных:
session_register("myvar1", "myvar2");
Использование переменных сеанса.Чтобы сделать переменную сеанса доступной для использования, сначала необходимо запустить сеанс, воспользовавшись одним из описанных выше способов. После этого появляется доступ к этой переменной. Если опция register_globals включена, то доступ к этой переменной можно получить через сокращенную форму ее имени, например, $myvar. Если же упомянутая опция не включена, получить доступ к переменной можно через ассоциативный массив $HTTP_SESSION_VARS, например, $HTTP_SESSION_VARS ["myvar"].
Переменные сеанса не могут быть перезаписаны данными GET или POST. Это хорошо с точки зрения обеспечения безопасности, однако сопряжено с некоторыми ограничениями при кодировании.
С другой стороны, от вас потребуется тщательность при проверке на предмет того, установлены ли уже переменные сеанса (например, с использованием isset() либо empty()). Кроме того, следует иметь в виду, что переменные могут быть установлены пользователем через GET или POST. Проверить, является ли переменная зарегистрированной переменной сеанса, можно обратившись к функции session_is_registered(). Вызов функции выполняется следующим образом:
$result = session_is_registered("myvar");
Эта функция проверит, является ли $myvar зарегистрированной переменной сеанса, и вернет true или false. Можно поступить и по-другому — проверить массив $HTTP_SESSION_VARS на предмет наличия в нем переменной.
Отмена регистрации переменных и завершение сеанса.После окончания работы с переменной сеанса ее регистрацию можно отменить, воспользовавшись функцией
session_unregister("myvar");
Подобно функции регистрации, эта функция требует указания имени переменной, регистрацию которой необходимо отменить, в виде строки, не включающей символ $. Данная функция за один раз может отменить регистрацию только одной переменной сеанса (в противоположность session_register()). Однако, для отмены регистрации всех переменных текущего сеанса можно обратиться к session_unset().
По завершении сеанса сначала потребуется отменить регистрацию всех переменных, а затем для обнуления идентификатора сеанса вызвать
session_destroy();
Пример простого сеанса.Изложенный выше материал может показаться несколько абстрактным, поэтому сейчас вашему вниманию предлагается пример. Приведенный в нем код обеспечивает обработку трех страниц. На первой странице мы запустим сеанс и зарегистрируем переменную $sess_var Код показан в листинге 6.15.
Листинг 6.15. page1.php — запуск сеанса и регистрация переменной
<?
session_start ();
session_register("sess_var");
$sess_var = "Hello world!";
echo "The content of \ $sess_var is $sess_var<br>" ;
?>
<a href = "page2.php">Next page</a>
Мы зарегистрировали переменную и установили ее значение.
Заметим, что мы изменили значение переменной уже после ее регистрации. Можно, однако, сделать и наоборот — установить значение, а после этого зарегистрировать переменную. Конечное значение переменной на странице — это то значение, которое будет доступно на последующих страницах. В конце сценария переменная сеанса преобразуется в последовательную форму (сериализуется), или замораживается, до своей перезагрузки через следующий вызов session_start(). Таким образом, следующий сценарий начинается с вызова session_start(). Сценарий показан в листинге 6.16
Листинг 6.16. page2.php — получение доступа к переменной сеанса и отмена регистрации
<?
session_start();
echo "The content of \ $sess_var is $sess_var<br>";
session_unregister("sess_var");
?>
<a href = "page3.php">Next page</a>
После вызова session_start() переменная $sess_var станет доступной, а ее значением будет то, которое сохранено в предыдущем сеансе.
Сделав с переменной все необходимые действия, мы вызываем session_unregister() для отмены ее регистрации. Обратите внимание: сеанс еще существует, но переменная $sess_var уже больше не является зарегистрированной.
И наконец, мы переходим к page3.php, последнему сценарию в рассматриваемом примере.
Листинг 6.17. page3.php — завершение сеанса
<?
session_start();
echo "The content of \ $sess_var is $sess_var<br>";
session_destroy () ;
?>
Как можно видеть, доступа к значению $sess_var больше нет. И в завершение — вызов session_destroy() для разрушения идентификатора сеанса.
Конфигурирование управления сеанса.А сейчас мы предлагаем ознакомиться с набором опций конфигурации для сеансов, которые можно установить в своем файле php.ini. В табл. 7.8 перечисляются некоторые из наиболее полезных опций вместе с их кратким описанием.
Таблица 6.8. Опции конфигурации сеанса
Имя опии и | Значение no умолчанию | Действие |
session.auto_start | 0 (запретить) | Автоматический запуск сеансов. |
session.cache_expire | Установка времени жизни для кэшированных станиц сеанса (в минутах). | |
session.cookie_domain | none | Домен для установки в cookie-наборе сеанса. |
session.cookie_lifetime | Определяет продолжительность существования cookie-набора идентификатора сеанса на машине пользователя. По умолчанию 0 — пока не будет закрыт браузер. | |
session.cookie_pafh | / | Путь для установки в cookie-наборе сеанса. |
session.name | PHPSESSID | Имя сеанса, которое в системе пользователя используется как имя cookie-набора. |
session.save_handler | файлы | Определяет место хранения данных сеанса. Здесь можно указать базу данных, однако для этого потребуется реализовать собственные функции. |
session.save_path | /tmp | Путь к месту хранения данных сеанса. В более общем случае для определения и обработки передаваемых на хранение аргументов используется session.save_handler. |
session.use_cookies | 1 (разрешить) | Конфигурация сеанса с возможностью использования cookie-наборов на стороне клиента. |
Выполнение аутентификации пользователей средствами управления сеансом.В завершение рассмотрим более важный пример использования контроля сеанса.
Наиболее часто, пожалуй, управление сеансом применяется в целях отслеживания пользователей после того, как они были аутентифицированы через механизм входной регистрации. В предлагаемом примере можно видеть, как эти функциональные возможности обеспечиваются за счет сочетания аутентификации при помощи базы данных MySQL и использования механизма управления сеансом.
В нашем примере мы воспользуемся базой данных аутентификации, которая была создана ранее. Это требуется для работы с модулем mod_auth_mysql. Пример включает три простых сценария. Первый, authmain.php, обеспечивает форму для входной регистрации и аутентификации пользователей Web-сайта. Второй, members_only.php, представляет информацию только для тех пользователей, которые успешно прошли входную регистрацию. Третий, logout.php. реализует выход пользователей из системы.
Первая страница предоставляет пользователю возможность войти в систему. В случае, если он предпримет попытку получить доступ к секции Members, не пройдя входную регистрацию, будет выдано сообщение. Если же пользователь сначала прошел входную регистрацию (с именем пользователя: testuser и паролем: test123, как было задано ранее), а потом попытался войти на страницу Members, он увидит соответствующее сообщение.
Давайте посмотрим на код приложения. Большая часть кода сосредоточена в сценарии authmain.php, приведенном в листинге 6.18. Давайте изучим его более подробно.
Листинг 6.18. authmain.php — Основная часть приложения аутентификации
session_start();
if ($userid && $password)
{
// если пользователь как раз пытается зарегистрироваться
$db_conn = mysql_connect("localhost", "webauth", "webauth");
mysql_select_db("auth", $db_conn);
$query = "select * from auth where name='$userid'"
." and pass=password('$password')";
$result = mysql_query($query, $db_conn);
if (mysql_num_rows($result) >0 )
{// если пользователь найден в базе данных,
// зарегистрировать его идентификатор
$valid_user = $userid;
session_register{"valid_user"); }
}
?>
<html><body><h1>Home page</h1>
<?
if (session_is_registered("valid_user"))
{
echo "You are logged in as: $valid_user <br>";
echo "<a href=\"logout.php\">Log out</a><br>";
}
else
{
if (isset($userid)) {
// если пользователь пытался зарегистрироваться,
// но возникла ошибка
echo "Could not log you in"; }
else {
// если пользователь либо не пытался зарегистрироваться,
// либо покинул сайт
echo "You are not logged in.<br>"; }
// форма для аутентификации
echo "<form method=post action=\"authmain.php\">";
echo "<table>"; echo "<tr><td>Userid:</td>";
echo "<td><input type=text name=userid></td></tr>";
echo "<tr><td>Password:</td>";
echo "<td><input type=password name=password></td></tr>";
echo "<tr><td colspan=2 align=center>" ;
echo "<input type=submit value=\"Log in\"></td></tr>" ;
echo "</table></form>" ;
}
?> <br>
<a href="members_only.php">Members section</a>
</body></html>
Данный сценарий отличается сложной (в разумных пределах) логикой, но иначе нельзя: ведь он осуществляет представление формы для входной регистрации и ее обработку.
Работа этого сценария сосредоточена вокруг переменной сеанса $valid_user. Основная идея здесь заключается в следующем: если кто-либо успешно прошел процедуру входной регистрации, мы регистрируем переменную сеанса с именем $valid_user, которая содержит идентификатор пользователя.
Так что же первым делом выполняется в сценарии? Правильно, вызов session_start(). Эта функция загружает переменную сеанса $valid_user, если последняя была зарегистрирована.
При первом проходе по сценарию ни один из условных операторов if не сработает и неудачливому пользователю к концу сценария останется лишь внимательно прочесть сообщение о том, что он не прошел процедуру входной регистрации. После этого мы предоставляем ему форму, при помощи которой он сможет это сделать:
echo "<form method=post action=\ "authmain.php\ ">";
echo "<table>";
echo "<tr><td>Userid:</td>";
echo "<td><input type=text name=userid></td></tr>" ;
echo "<tr><td>Password:</td>";
echo "<td><input type=password name=password></td></tr>" ;
echo "<tr><td colspan=2 align=center>" ;
echo "<input type=submit value=\ "Log in\ "></td></tr>" ;
echo "</table></form>";
Когда пользователь нажмет кнопку отправки (Submit), сценарий вызывается заново и вновь все начинается с начала. На этот раз в нашем распоряжении будут имя пользователя и пароль, позволяющие его аутентифицировать (они хранятся в $userid и $password). Если эти переменные установлены, переходим к блоку аутентификации:
if ($userid && $password) {
// если пользователь как раз пытается зарегистрироваться
$db_conn = mysql_connect("localhost", "webauth", "webauth");
mysql_select_db("auth", $db_conn);
$query = "select * from auth "
."where name='$userid' " . " and pass=password('$password')";
$result = mysql_query($query, $db_conn);
И вот мы подключаемся к базе данных MySQL и проверяем имя пользователя и пароль. Если в базе данных существует соответствие этой паре, мы регистрируем переменную $valid_user, которая содержит идентификатор для конкретного пользователя. Таким образом, мы знаем, кто вошел в систему и, соответственно, будем его отслеживать.
if (mysql_num_rows($result) >0) {
// если пользователь найден в базе данных,
// зарегистрировать его идентификатор
$valid_user = $userid;
session_register("valid_user"); }
Поскольку уже известно, кто сейчас посещает сайт, то повторно предоставлять ему aорму входной регистрации нет необходимости. Вместо этого мы сообщаем пользователю, что мы знаем, кто он такой, и даем ему возможность выхода из системы:
if (session_is_registered("valid_user")) {
echo "You are logged in as: $valid user <br>";
echo "<a href=\"logout.php\">Log out</a><br>"; }
Если же при попытке произвести входную регистрацию пользователя, мы по какой-то причине терпим неудачу, то у нас имеется идентификатор пользователя, но нет переменной $valid_user, и ничего не остается, кроме как выдать сообщение об ошибке:
if (isset($userid)) {
// если пользователь пытался зарегистрироваться, но возникла ошибка
echo "Could not log you in"; }
Поскольку $valid_user является зарегистрированной переменной сеанса, ее нельзя перезаписать путем передачи другого значения через URL, например так:
members_only.php?valid_user=testuser
С основным сценарием, похоже, все понятно. А теперь посмотрим на страницу Members. Код этого сценария показан в листинге 6.19.
Листинг 6.19. members_only.php — процедур проверки достоверности пользователя
<?
session_start();
echo "<h1>Members only</h1>";
// проверить переменные сеанса
if (session_is_registered("valid_user"))
{
echo "<p>You are logged in as $valid_user.</p>";
echo "<p>Members only.content goes here</p>";
}
else
{
echo "<p>You are not logged in.</p>";
echo "<p>Only logged in members may see this page.</p>";
}
echo "<a href=\"authmain.php\">Back to main page</a>";
?>
Приведенный выше код очень прост. Все, что он делает — это запуск сеанса и проверка того, содержит ли текущий сеанс зарегистрированного пользователя, с использованием функции session_registered_user(). Если пользователь прошел процедуру входной регистрации, мы отображаем содержимое сайта для зарегистрированных пользователей, в противном случае мы сообщаем ему, что у него нет соответствующих полномочий.
И в завершение рассмотрим сценарий logout.php, который завершает регистрацию пользователя в системе. Код сценария показан в листинге 6.20.
Листинг 6.20. logout.php —отмена регистрации переменных сеанса и завершение сеанса
<?
session_start();
$old_user = $valid_user; // сохранить для проверки,
// регистрировался ли пользователь
$result = session_unregister("valid_user");
session_destroy();
?>
<html><body><h1>Log out</h1>
<?
if (! empty ($old_user) ){
if ($result){
// если пользователь был зарегистрирован и не покинул систему
echo "Logged out.<br>"; }
else { // если пользователь не может покинуть систему
echo "Could not log you out.<br>"; } }
else { // если пользователь не был зарегистрирован,
echo "You were not logged in, and so have not been logged out <br>"; }
?>
Приведенный код очень прост. Мы запускаем сеанс, запоминаем старое имя пользователя, отменяем регистрацию переменной $valid_user и завершаем сеанс. После этого мы выдаем пользователю одно из следующих сообщений: он вышел из системы, не может выйти из системы или не может выйти из системы, поскольку первоначально даже не регистрировался.
Примеры программ.
Дата добавления: 2015-11-04; просмотров: 1792;