Пароль на страницу. Часть 5. Сессии
DL
9.8.2001
Зачем я писал заметку про куки? "Не понимаю, зачем писать про куки, когда в php есть сессии?!" Затем, чтобы у читателей не образовывалась перед глазами плоская картина. Не везде ещё стоит php 4-й версии, а в третьей они не поддерживаются. Более того, не везде сессии так необходимы? за редким исключением алгоритм авторизации проверяет правильность логина/пароля и правильность данных сессии, а затем либо отфутболивает клиента на страницу входа, либо берёт массив (или объект) с данными о пользователе.
Случаев, когда работа сессиями необходима, не так уж и часты. Например, в своей игре "Монополист" я сразу стал использовать сессии, потому что пользователь может играть в нескольких играх и одна и та же страница в одном и том же сеансе работы может содержать разные данные. Там лучше данные для одной из игр, в которых пользователь участвует, хранить в сессии и сделать страницу для перехода между играми.
В общем, я не утверждаю, что сессиями пользоваться не нужно. Нужно, только всему своё место. К вопросу применимости трёх способов авторизации ? через 401-й заголовок ("realm"), куки или сессии ? я вернусь позже. Сейчас поговорю о сессиях.
Сессии в php ? это на самом деле не метод авторизации (само понятие неправильное, но в форумах спрашивают именно "как авторизовывать пользователя через сессии?"). Встроенный в php механизм пользовательских сессий лишь идентифицирует этих пользователей, авторизовывать ? опять же, работа вашего скрипта.
Много про механизм сессий рассказывать не буду ? уже рассказано. В самом простом виде (вернее в самом dafault-ном) механизм этот работает так: система держит на сервере файл сессии, в котором содержатся её переменные. Пользователь при запуске сессии получает уникальный идентификатор (обычно через куку), и при обращении к другим страницам отправляет её. При запуске механизма сессий в вашем скрипте обработчик php проверяет, существует ли файл соответствующий пришедшему идентификатору сессии ? если существует, то скрипт сможет прочесть данные из файла, если нет ? будет запущена новая сессия и создан файл. Разумеется, имя данной переменной опеределено в установках php.
Теперь о том, какими функциями мы пользуемся.
session_start(). Запускает сам механизм сессий. От пользователя должна быть переменная и соответствующий ей файл. Если нет файла, он создаётся, и сессия запускается с нуля. Если нет ни файла, ни переменной, то генерируется переменная (например, посылается заголовок с кукой) и создаётся файл.
session_register(имя1, имя2, имя3...). Указание, какие переменные запомнить в файле по окончании работы скрипта. После того как пользователь перейдёт к другой странице, можно запустить механизм сессий, и после вызова данной функции переменные будут доступны.
session_destroy(). Удаляет файл данных сессии (при использовании кук надо удалять их вручную, выставив пустую куку: "setcookie(session_name())").
session_end(). Если после авторизации данные о пользователе менять не надо, лучше сразу "выключить за собой свет" ? закрыть файл и освободить доступ к нему.
session_set_cookie_params(жизнь, путь, домен). Установка параметров куки с идентификатором сессии (по умолчанию кука выставляется на корень сервера и на 0 секунд ? до закрытия браузера).
Пока всё. Подробно про сессии будут отдельные выпуски. Пока опишу механизм авторизации и идентификации пользователя при помощи сессий.
Итак, имеем три файла ? вход (login), проверка (auth) и выход (logout).
// вырезка всех нежелательных символов
$login = preg_replace("/[^\w_\.\-]/", "", $HTTP_POST_VARS["login"]);
$pass = trim($HTTP_POST_VARS["pass"]);
// проверка переменных
if (strlen($login)==0 strlen($pass)==0)
$error = "Введите логин и пароль";
else {
// проверка логина и пароля
$user_result = mysql_query("SELECT * FROM user WHERE login='$login' AND pass='". md5($pass). "'");
/* если возникла ошибка в базе (например, пользователь всунул в сессию дли-и-инную переменную, которую база переваривать не захотела) или получилась не одна строка, отфутболиваем пользователя */
if (mysql_error())
die(mysql_error());
elseif (@mysql_num_rows($user_result) != 1)
$error = "Неверное имя пользователя или пароль.";
// если всё нормально, выбираем данные, запускаем сессию
else {
$user = mysql_fetch_assoc($user_result);
session_set_cookie_params(1800, "/");
session_start();
// запоминаем данные о пользователе
session_register("user");
// и дальше отправляем его куда-нибудь
if (isset($HTTP_POST_VARS["return"]))
header("Location: {$HTTP_POST_VARS['return']}");
else
header("Location: /");
exit();
};
};
/* здесь пользователь уже не прошёл авторизацию, но может отправить куку из закрытой сессии. очистим её. */
if (isset($HTTP_COOKIE_VARS[session_name()]))
setcookie(session_name());
// дальше рисуем форму, это неинтересно.
Данный скрипт является и обработчиком и формой для ввода данных. При получении логина и пароля он обрабатывает их и, если они правильные, прекращает работу, отправив пользователя на нужную страницу. Если данные неправильные или вообще отсутствуют, рисует форму.
/* убиваем переменную user, чтобы нельзя было, нарисовав форму, отправить данные в post-запросе. */
unset($user);
// флаг "ошибка сессии" ? если он включён, работа прекратится.
$session_error = false;
// если не существует куки с идентификатором сессии, поднять флаг
if (!isset($HTTP_COOKIE_VARS[session_name()]))
$session_error = true;
// если существует, запускаем механизм сессий и регистрируем переменную $user.
else {
session_start();
session_register("user");
/* если случайно в массиве нет логина и пароля, работа тоже прекращается ("ничего не знаем, мы вам их давали") */
if (!isset($user["login"]) !isset($user["pass"]))
$session_error = true;
};
/* если пользователю до сих пор удалось геройски избежать ошибок, делается проверка через базу так же, как и на входе. */
if (!$session_error) {
$check_result = mysql_query("SELECT uid FROM user WHERE login='{$user[login]}' AND pass='{$user[pass]}'");
if (mysql_error() @mysql_num_rows($user_result) != 1)
$session_error = true;
};
// если была какая-то ошибка, то
if ($session_error) {
// уничтожаем данные сессии
session_destroy();
// уничтожаем куку, если она была
if (!isset($HTTP_COOKIE_VARS[session_name()]))
setcookie(session_name(),"","/");
/* отправляем пользователя на вход, с возможностью вернуться на запрошенный адрес */
header("Location: /login.php?return=$REQUEST_URI");
// прекращаем работу
exit();
};
mysql_free_result($check_result);
Пользователь проверен и в массиве $user ? все данные о нём, можно, например, поприветствовать его по имени-отчеству:
<?
include("auth.inc");
?><html>
<head><title><? print ("Здравствуйте, {$user[fname]} {$user[sname]}!"); ?></title></head>
<body>
[skip]
И выход:
if(isset($HTTP_COOKIE_VARS[session_name()])) {
// запуск механизма сессий
session_start();
// удаление файла
session_destroy();
// удаление куки
setcookie(session_name());
};
// выход со страницы
header("Location: /login.php");
Пара замечаний: закрываемая паролем часть в данном примере - весь сервер (например, service.firm.ru), для закрытия директории нужно исправить пути. Вместо PHPSESSID используется session_name(), чтобы можно было свободно менять имя идентификатора. Кстати, на одном физическом сервере можно делать разные имена идентификаторов сессий - достаточно в нужную часть положить файл .htaccess со строкой php_value session.name "ABRACADABRA".
На этом всё. Ждите статьи со сравнением приведённых способов авторизации (свои куки, сессии и 401-й код) и нескольких статей по сессиям.