PHP в деталях

       

Пароль на страницу. Часть 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-й код) и нескольких статей по сессиям.


Содержание раздела