Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Самоучитель PHP 4 - Котеров Д. В.

.pdf
Скачиваний:
99
Добавлен:
24.05.2014
Размер:
4 Мб
Скачать
☆

68

Часть I. Основы Web-программирования

времени узнать, что же там у пользователя установлено. Самым удобным в Cookies является то, что они могут храниться недели и годы до тех пор, пока их не обновит сервер или же пока не истечет срок их жизни (который тоже назначается сценарием при создании Cookie). Таким образом, мы можем иметь Cookies, которые "живут" всего несколько минут (или до того момента, пока не закроют браузер), а можем — "долгожителей".

Не правда ли, последний способ представляет собой идеальное решение для нашей проблемы? Действительно, теперь сцеанарию гостевой книги достаточно получить у пользователя его данные, запомнить их в Cookies (как это сделать — см. ниже), а затем работать, будто ничего и не произошло. Конечно, перед выводом HTMLдокумента формы обязательно придется проставить значения value для некоторых элементов (которые, ясно, извлечены из соответствующих Cookies).

Но не все так гладко. Конечно, и у этой схемы есть недостатки. Первый из них — не все браузеры поддерживают Cookies, а пользователи тех, которые поддерживают, иногда имеют обыкновение отключать Cookies — якобы для большей безопасности (хотя безопасность тут совсем ни при чем, дело в самих этих пользователях). Второй недостаток заключается в том, что каждый браузер хранит свои Cookies отдельно. То есть Cookies, установленные при пользовании Internet Explorer, не будут "видны" при работе в Netscape, и наоборот.

Но, согласитесь, все же это почти не умаляет достоинств Cookies — в конце концов, обычно пользователи работают только в одном из перечисленных браузеров. Кстати, все чаще в Internet Explorer. На момент написания этих строк указанный браузер имеет в несколько раз большие возможности, чем Netscape (работая при этом, правда, несколько медленнее). Что ж... Время покажет, кто из них выживет.

Но я несколько отклонился от темы. Как уже упоминалось, каждому Cookie сопоставлено время его жизни, которое хранится вместе с ним. Кроме этого, имеется также информация об имени сервера, установившего этот Cookie, и URL каталога, в котором находился сценарий-хозяин в момент инициализации (за некоторыми исключениями).

Зачем нужны имя сервера и каталог? Очень просто: дело в том, что сценарию передаются только те Cookies, у которых параметры с именем сервера и каталога совпадают соответственно с хостом и каталогом сценария (ну, на самом деле каталог не должен совпадать полностью, он может являться подкаталогом того, который создан для хранения Cookies). Так что совершенно невозможно получить доступ к "чужим" Cookies — браузер просто не будет посылать их серверу. Это и понятно: представьте себе, сколько ненужной информации передавалось бы сценарию, если бы все было не так (особенно если пользователь довольно активно посещает различные серверы, которые не прочь поставить ему свой набор Cookies). Кроме того, дополнительные сведения предоставляются в целях защиты информации от несанкционированного доступа — ведь в каком-то Cookie может храниться, скажем, важный пароль (как часто делается при авторизации), а он должен быть доступен только одному определенному хосту.

Глава 3. CGI изнутри

69

Установка Cookie

Мы подошли к вопросу: как же сценарий может установить Cookie в браузере пользователя? Ведь он работает "на одном конце провода", а пользователь — на другом. Решение довольно логично: команда установки Cookie — это просто один из заголовков ответа, передаваемых сервером браузеру. То есть, перед тем как выводить Content-type, мы можем указать некоторые команды для установки Cookie. Выглядит такая команда следующим образом (разумеется, как и всякий заголовок, записывается она в одну строку):

Set-Cookie: name=value; expires=дата; domain=имя_хоста; path=путь; secure

Существует и другой подход активизировать Cookie — при помощи HTML-тэга <meta>. Соответственно, как только браузер увидит такой тэг, он займется обработкой Cookie. Формат тэга такой:

<meta http-equiv="Set-Cookie"

content="name=value; expires=дата; domain=имя_хоста; path=путь; secure"

>

Мы можем видеть, что даже названия параметров в этих двух способах одинаковы. Какой из них выбрать — решать вам: если все заголовки уже выведены к тому моменту, когда вам потребовалось установить Cookie, используйте тэг <meta>. В противном случае лучше взять на вооружение заголовки, т. к. они не видны пользователю, а чем пользователь меньше видит при просмотре исходного текста страницы в браузере — тем лучше нам, программистам.

Возможно, вы спросите, нахмурив брови: "Что же, с точки зрения программиста хороший пользователь — слепой пользователь?" Тогда я отвечу: "Что вы, нет и еще раз нет! Такой пользователь хорош лишь для дизайнера, для програм- миста же желателен пользователь безрукий (или, по крайней мере, лишенный клавиатуры и мыши)".

Вот что означают параметры Cookie:

name

Вместо этой строки нужно задать имя, закрепленное за Cookie. Имя должно быть URL-кодированным текстом, т. е. состоять только из алфавитно-цифровых символов. Впрочем, обычно имена для Cookies выбираются именно так, чтобы их URLкодированная форма совпадала с оригиналом.

value

Текст, который будет рассматриваться как значение Cookie. Важно отметить, что этот текст (ровно как и строка названия Cookie) должен быть URL-кодирован. Таким об-

70

Часть I. Основы Web-программирования

разом, я должен отметить неприятный факт, что придется писать еще и функцию URL-кодирования (которая, кстати, раза в 2 сложнее, чем функция для декодирования, т. к. требует дополнительного выделения памяти).

expires

Необязательная пара expires=дата задает время жизни нашего Cookie. Точнее, Cookie самоуничтожится, как только наступит указанная дата. Например, если задать expires=Friday,31-Dec-99 23:59:59 GMT, то "печенье" будет "жить" только до

31 декабря 1999 года. Кстати, вот вам и вторая неприятность: хорошо, если мы знаем наверняка время "смерти" Cookie. А если нам нужно его вычислять на основе текущего времени (например, если мы хотим, чтобы Cookie существовал 10 дней после его установки, как в подавляющем большинстве случаев и происходит)? Придется использовать функцию, которая формировала бы календарную дату в указанном выше формате. Кстати, если этот параметр не указан, то временем жизни будет считаться вся текущая сессия работы браузера, до того момента, как пользователь его закроет.

domain

Параметр domain=имя_хоста задает имя хоста, с которого установили Cookie. Ранее я уже говорил про этот параметр. Так вот, оказывается, его можно менять вручную, прописав здесь нужный адрес, и таким образом "подарить" Cookie другому хосту. Только в том случае, если параметр не задан, имя хоста определяется браузером автоматически.

path

Параметр path=путь обычно описывает каталог (точнее, URI), в котором расположен сценарий, установивший Cookie. Как мы видим, этот параметр также можно установить вручную, записав в него не только каталог, а вообще все, что угодно. Однако при этом следует помнить: указав хост, отличный от хоста сценария, или путь, отличный от URI каталога (или родительского каталога) сценария, мы тем самым никогда больше не увидим наш Cookie в этом сценарии.

secure

Этот параметр связан с защищенным протоколом передачи HTTPS, который в книге не рассматривается. Если вы не собираетесь писать сценарии для проведения банковских операций с кредитными карточками (или иные, требующие повышенной безопасности), вряд ли стоит обращать на него внимание.

После запуска сценария, выводящего соответствующий заголовок (или тэг <meta>), у пользователя появится Cookie с именем name и значением value. Еще раз напоминаю: значения всех параметров Cookie должны быть URL-кодированы, в противном случае возможны неожиданности.

Глава 3. CGI изнутри

71

Получение Cookies из браузера

Получить Cookies для сценария несколько проще: все они хранятся в переменной окружения HTTP_COOKIE в таком же формате, как и QUERY_STRING, только вместо & используется ;. Например, если мы установили два Cookies: cookie1=value1 и cookie2=value2, то в переменной окружения HTTP_COOKIE будет следующее:

cookie1=value1;cookie2=value2.

Сценарий должен разобрать эту строку, распаковать ее и затем работать по своему усмотрению.

Пример программы для работы с Cookies

В заключение приведу простой сценарий, который использует Cookies. Для упрощения в нем не производится URL-кодирование и декодирование — будем считать, что пользователь может печатать только на латинице.

Листинг 3.8. Простой сценарий, использующий Cookies

#include <stdio.h> #include <stdlib.h>

//начало программы void main() {

//Временный буфер char Buf[1000];

//получаем в переменную Cook значение Cookies char *Cook = getenv("HTTP_COOKIE");

//пропускаем в ней 5 первых символов ("cook="), если она не пустая –

//получим как раз значение Cookie, которое мы установили ранее

//(см. ниже).

Cook += 5; // сдвинули указатель на 5 символов вперед по строке

//получаем переменную QUERY_STRING char *Query = getenv("QUERY_STRING");

//проверяем, заданы ли параметры у сценария — если да, то

//пользователь, очевидно, ввел свое имя или нажал кнопку,

//в противном случае он просто запустил сценарий без параметров if(strcmp(Query, "")) { // строка не пустая?

//копируем в буфер значение QUERY_STRING,

72

Часть I. Основы Web-программирования

//пропуская первые 5 символов (часть "name=") -

//получим как раз текст пользователя strcpy(Buf, Query + 5);

//Пользователь ввел имя — значит, нужно установить Cookie printf("Set-cookie: cook=%s; "

"expires=Friday,31-Dec-01 23:59:59 GMT", Buf);

//Теперь это — новое значение Cookie

Cook=Buf;

}

//выводим страницу с формой printf("Content-type: text/html\n\n"); printf("<html><body>\n");

//если имя задано (не пустая строка), приветствие if(strcmp(Cook, ""))

printf("<h1>Привет, %s!</h1>\n",Cook);

//продолжаем

printf("<form action=/cgi-bin/script.cgi method=get>\n"); printf("Ваше имя: ");

printf("<input type=text name=name value=’%s’>\n",Cook); printf("<input type=submit value=’Отправить’>\n"); printf("</form>\n");

printf("</body></html>");

}

Теперь при первом заходе на этот URL пользователь получит форму с пустым полем для ввода имени. Если он что-то туда напечатает и нажмет кнопку отправки, его информация запомнится браузером. Итак, посетив в любое время до 31 декабря 2001 года этот же URL, он увидит то, что напечатал давным-давно в текстовом поле. И, что самое важное, — его информацию "увидит" также и сценарий. Кстати, у злоумышленника нет никаких шансов получить значение Cookie посетителя, потому что оно хранится у него на компьютере, а не на сервере.

И опять я намекаю на то, что использование Си и на этот раз довольно затруднительно. Неудобно URL-декодировать и кодировать при установке Cookies, накладно разбирать их на части, да и вообще наша простая программа получилась слишком длинной. Не правда ли, приятно будет обнаружить, что в PHP все это реализовано автоматически: для работы с Cookies существует всего одна универсальная функция SetCookie(), а получение Cookies от браузера вообще не вызовет никаких проблем, потому что оно ничем не отличается от получения данных формы. Это логично. В

Глава 3. CGI изнутри

73

самом деле, какая нам разница, какие данные пришли из формы, а какие — из Cookies? С точки зрения сценария — все равно...

Но не буду забегать вперед. Займемся пока теорией авторизации.

Авторизация

Часто бывает нужно, чтобы на какой-то URL могли попасть только определенные пользователи. А именно, только те, у которых есть зарегистрированное имя (login) и пароль (password). Механизм авторизации как раз и призван упростить проверку данных таких пользователей.

Я не буду здесь рассматривать все возможности этого механизма по трем причинам. Во-первых, существует довольно много типов авторизации, различающихся степенью защищенности передаваемых данных. Во-вторых, при написании обычных CGIсценариев для того, чтобы включить механизм авторизации, необходимо провести некоторые манипуляции с настройками (файлами конфигурации) сервера, что, скорее всего, будет затруднительно (ведь обычно компания, которая предоставляет услуги по обслуживанию виртуального хоста, не позволяет вмешиваться в настройки сервера). И наконец, в-третьих, весь механизм авторизации значительно упрощается и унифицируется при использовании PHP, и вам не придется ничего исправлять в этих злополучных настройках сервера. Так что давайте отложим практическое знакомство с авторизацией и займемся ее теорией.

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

WWW-Authenticate: Basic realm="имя_зоны"

HTTP/1.0 401 Unauthorized"

Обратите внимание на то, что последний заголовок несколько отличается по форме от обычных заголовков. Так и должно быть. Строка имя_зоны в первом из них задает некоторый идентификатор, который будет определять, к каким ресурсам будет разрешен доступ зарегистрированным пользователям. При программировании CGIсценариев этот параметр используется в основном исключительно для формирования приветствия (подсказки) в диалоговом окне, появляющемся в браузере пользователя (там отображается имя зоны), так что мы не будем вдаваться в детали относительно него.

Затем, как обычно, посылается тело документа (сразу отмечу, что именно это тело ответа будет выдано пользователю, если он нажмет в диалоговом окне (см. ниже) кнопку Cancel, т. е. отменит вход). В этом случае происходит нечто удивительное: в браузере пользователя появляется небольшое диалоговое окно, в котором предлагается вести login и password. После того как пользователь это сделает, управление пере-

74

Часть I. Основы Web-программирования

дается обратно серверу, который среди обычных заголовков запроса (которые посылает браузер) получает примерно такой:

Authorization: Basic TG9naW46UGFzcw==

Это — ни что иное, как закодированные данные, введенные пользователем. Теоретически, далее этот заголовок должен каким-то образом передаться сценарию (для этого как раз и необходимо добавление команд в файлы конфигурации сервера). Сценарий, декодировав его, может решить: то ли повторить всю процедуру сначала (если имя или пароль неправильные), или же начать работать с сообщением "OK, все в порядке, вы — зарегистрированный пользователь".

Предположим, что сценарий подтвердил верность данных и "пропустил" пользователя. В этом случае происходит еще одна вещь: login и password пользователя запоминаются в скрытом Cookie, "живущем" в течение одной сессии работы с браузером. Затем, что бы мы ни делали, заголовок

Authorization: Basic значение_Cookie

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

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

ЧАСТЬ II.

ВЫБОР И НАСТРОЙКА ИНСТРУМЕНТАРИЯ. WEB-СЕРВЕР APACHE