cookie

Значение: строковое Чтение/Запись

Совместимость: WinLE3+, MacIE3+, NN2+, Mozl+, Safaril+

В Web-браузере механизм cookie-значений позволяет хранить небольшие объемы информации о компьютере клиента в довольно безопасной форме. Другими словами, если необходимо получить определенную информацию о клиенте во время загрузки различных HTML-документов (причем эта информация должна использоваться между сеансами запуска браузера), используют механизм cookie. Как правило, cookie применяется для хранения имени пользователя и пароля, который он вводит в соответствующее поле Web-узла, защищенного паролем. При первичном вводе данной информации в CGI-форму, используя Netscape Navigator, программа CGI производит запись информации в файлы cookie, сохраняемые на жестком диске пользователя (обычно после шифрования пароля). В следующий раз, когда пользователь решит перейти на тот же узел, ему не нужно будет опять вводить свои данные. Сервер найдет cookie-файл, сохраненный именно для этого сервера, и извлечет содержащиеся там регистрационное имя и пароль, которые затем будут введены автоматически.

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

Файл cookie

Некоторые CGI-программы могут как считывать, так и записывать данные на жесткий диск пользователя. Вам необходимо решить, использовать их или нет. Механизм cookie, содержащийся в браузере, не позволяет использовать папки пользователя для всеобщего доступа (или проводить незаконные операции в них). Вместо этого он обеспечивает доступ только к одному определенному текстовому файлу (в NN/Mozilla/Safari) или набору текстовых файлов (в Internet Explorer), которые сохраняются в строго определенном месте жесткого диска пользователя (в зависимости от операционной системы).

Файл cookie представляет собой обычный текстовый файл (но в Netscape Navigator для Macintosh файл Magi с Cookie не текстовый, поэтому пользователи Macintosh могут открыть его только с помощью специальных приложений). Для того чтобы просмотреть cookie-файл, рекомендуется предварительно сделать копию в другой папке и только после этого его открывать. Любое изменение существующего файла может испортить содержащуюся в нем ценную запись, использование которой предоставляет доступ к регулярно посещаемому данным пользователем узлу. Формат данных cookie в Netscape Navigator и Internet Explorer разный, что зависит от разных методик, которые используются в них для регистрации cookie-записей. В файле Netscape Navigator (после нескольких закомментированных строчек, которые предупреждают о недопустимости изменения пользователем файла вручную) приводится текст в виде таблицы. Каждая строка содержит информацию одной записи. На первый взгляд, cookie-файл ничем не отличается от обычной базы данных. В Internet Explorer в cookie-файле сохраняются те же данные, что и в Netscape Navigator, но сами записи представлены в виде списка. Структура этих файлов не имеет особого значения для занесения в них данных, так как оба браузера используют одинаковый синтаксис чтения и записи, применяя свойство document.cookie.

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

cookie-запись

Каждая cookie-запись содержит следующие "поля" (не обязательно в таком же порядке).

Заметьте, что записи зависят только от доменов. Другими словами, если запись создается одним доменом, другой домен не сможет считать эту запись, используя обычный cookie-формат. Поэтому совершенно безопасно хранить так называемые невосстанавливаемые пароли (пары имя пользователя-пароль, необходимые для доступа к некоторым узлам со свободной регистрацией) в записях. Кроме того, пароли к узлам, хранящиеся в записях, обычно имеют вид зашифрованных строк. Это усложняет доступ к информации, находящейся в cookie-файле, если компьютер оставлен без присмотра. Таким образом, вычислить личный пароль пользователя оказывается довольно трудно.

Кстати, cookie-запись имеет срок службы. Поскольку в некоторых браузерах количество записей ограниченно (300 в Netscape Navigator), cookie-файл может за несколько лет переполниться. Поэтому, если во время работы браузера планируется сохранять cookie-запись, автор этой записи должен установить срок ее службы. Браузеры автоматически удаляют "просроченные" записи.

Однако не все записи должны создаваться во время работы браузера. Сценарии, в которых используются временные записи, при работе с Web-узлами имеют сходную структуру. Многие коммерческие узлы создают одну или несколько временных cookie-переменных для дальнейшего занесения в них названий тех товаров, которые пользователь намеревается приобрести. Эти элементы содержатся в виде копии в определенной форме. Но после представления этой формы серверу, данные о клиенте, содержащиеся в ней, не будут иметь никакого определенного значения. Следовательно, если сценарий не определяет дату истечения срока службы записи, браузер продолжает записывать новые cookie в свою кэш-память, не занося их в cookie-файл. Когда пользователь покидает браузер, данные cookie исчезают, как это и предполагалось.

Доступ из JavaScript

Доступ к записям cookie из JavaScript ограничен заданием cookie (с рядом необязательных параметров) и получением данных cookie (но уже без параметров).

Оригинальная объектная модель определяет cookie-записи как свойства документов, но такая формулировка не совсем корректна. При использовании заданного по умолчанию пути для установки cookie (текущая папка документа, при выполнении сценария из которого используется первая cookie-запись) все документы той же папки на том же сервере имеют доступ для записи и чтения к содержащимся там cookie-данным. Преимущество такого расположения состоит в следующем: если есть приложения, представленные несколькими документами, и все они обслуживаются из одной папки, то данные cookie могут использоваться ими совместно. Однако Netscape Navigator и Internet Explorer установили ограничение: 20 cookie-записей для каждого домена; в IE3 задается еще более строгое ограничение для одной записи (одна пара имя-значение) на домен. Если пользователя не устраивают поставленные условия, то ему необходимо найти другой способ записи cookie-данных.

Сохранение cookie-записей

Для записи данных в cookie-файл в JavaScript используется простой оператор, который назначает свойству document. cookie определенное значение. Но форматирование данных является ключевым моментом в осуществлении доступа к записям. Существует определенный синтаксис для переопределения cookie (необязательные элементы заключены в квадратные скобки).

document.cookie = "имяСооkiе = ДанныеСооkiе
   [; expires = времяФорматаGМТ]
   [; path=пyть]
   [; domain=имяДомена]
   [; secure]"

Имя/данные

Каждая cookie-запись должна иметь имя и строковое значение (даже если этому значению соответствует пустая строка). Такие пары имя-значение наиболее часто встречаются в HTML, но в соответствующем выражении они используются по отдельности. Например, если пользователь собирается сохранить слово "Фред" в cookie-записи с именем userName, выражение на JavaScript будет иметь такой вид.
document.cookie = "userName=Fred";

Если браузер не находит cookie-записи с таким именем в текущем домене, он создает ее автоматически; если запись с таким именем уже существует, браузер просто заменяет ее старые данные новыми. Поиск document. cookie в этом пункте выдает следующую строку.

userName=Fred

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

Полный набор такого типа должен быть представлен строкой, не содержащей точек с запятой, просто запятых или символов пробелов. Для того чтобы убрать пробелы между словами, предварительно обработайте значение записи функцией JavaScript escape(), которая переведет пробелы в ASCII-код, равный %20 (примените функцию unescape(), чтобы убедиться в правильности восстановления пробелов в случае возникновения такой необходимости).

Нельзя сохранять массив или объект JavaScript в cookie-записи. С помощью метода Array.join() преобразуйте массив в строку; используйте String.split() для создания этого массива заново после последнего прочтения полученной cookie-записи. Эти два метода доступны в NN3+, Mozillal+ и IE4+.

Срок службы (хранения)

Устанавливаемая дата срока службы записи должна соответствовать значению времени по Гринвичу (Greenwich Mean Time — GMT). Чтобы вычислить дату истечения срока службы записи, основанную на сегодняшней дате, используйте объект Date, представленный в JavaScript, следующим образом.

var exp = new Date();
var oneYearFromNow = exp.getTime() + (365 * 24 * 60 * 60 * 1000);
exp.setTime(oneYearFromNow);

Затем преобразуйте дату в принятый строковый формат GMT.

document.cookie = "userName=Fred; 
expires=" + exp.toGMTString();

В cookie-файле срок службы и время хранятся как числовые значения (в секундах), но для их установки необходимо использовать формат времени соответственно GMT. cookie-запись можно удалить прежде, чем истечет срок ее службы — установите для данной записи новый срок, уже прошедший относительно текущего времени и даты. Самый безопасный параметр срока хранения приведен ниже.

expires=Thu, Ol-Jan-70 00:00:01 GMT

Отсутствие даты срока службы сообщает браузеру о том, что эта cookie-запись является временной. Таким образом, браузер не записывает ее в cookie-файл, а при следующем запуске этого браузера она не будет существовать в его буфере.

Путь

Для cookie-записей, осуществляемых стороной клиента, наиболее удобно принять путь, заданный по умолчанию (в текущей папке). Конечно, можно создать дополнительную копию cookie в другой папке (для другого домена), таким образом, одни и те же данные будут доступны документу, расположенному в другой области Web-узла (или вообще сети).

Домен

Для синхронизации cookie-данных с определенным документом (или их группой) браузер определяет домен текущего документа и помещает в cookie-файл записи, соответствующие этому домену. Поэтому, если пользователь собирается просмотреть список всех cookie-данных, содержащихся в свойстве document. cookie, он должен просмотреть все наборы имя-значение, находящиеся в cookie-файле, с именем домена текущего документа. Если пользователь уверен, что документ не будет скопирован на другой сервер в пределах его домена, он может и не использовать параметр domain при сохранении cookie-записей. Автоматическое определение домена текущего документа и занесение его в cookie-файл производится по умолчанию. Необходимо знать, что в формате представления домена должно содержаться по крайней мере две точки.

.mcom.com
.hotwired.corn

В противном случае нужно записать полный URL-адрес домена, включая протокол http://.

Secure

Если при сохранении cookie-записи параметр SECURE отсутствует, подразумевается, что cookie-данные доступны для любого документа или CGI-программы, которая находится в узле пользователя, определяет другой домен и соответствующий ему путь. В процессе создания cookie-записей на стороне клиента при сохранении cookie этот параметр опускается.

Получение cookie-данных

Данные cookie, используемые в JavaScript, представляют собой одну-единственную строку, которая содержит все наборы имен и данных. Даже при том, что cookie-файл хранит параметры каждой cookie-записи, с помощью JavaScript можно получить только наборы имен и данных. Кроме того, когда две cookie-записи или более (максимум 20) удовлетворяют критериям домена, в JavaScript они будут представлены в одной строке данных, разграниченных точкой с запятой и пробелом. Например, строка document. cookie может выглядеть следующим образом.
userName = Fred; 
password = NikL2sPacU;

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

Если пользователь уверен, что в существующем файле содержится только одна cookie-запись (и к этому домену записи больше добавляться не будут), он может извлечь эту запись, используя ее имя. Например, данные cookie-записи, имя которой состоит из семи символов, можно извлечь, используя следующее выражение.

var data = unescape(document.cookie.substring(7,document.cookie.length));
Первый параметр метода substring() включает знак = для отделения имени от данных. Целесообразно создать функцию общего назначения, которая сможет управлять одной или несколькими cookie-записями. Далее приводится подобная функция, которую используют в сценариях некоторые разработчики.
function getCookieData(labelName) { 
  var labelLen = labelName.length; 
  //однократное чтение свойства cookie 
  var cookieData = document.cookie; 
  var cLen = cookieData.length; 
  var i = 0; 
  var cEnd;
  while (i < cLen) { 
    var j = i + labelLen;
    if (cookieData.substring(i,j) == labelName) { 
      cEnd = cookieData.indexOf(";",j); 
      if (cEnd == -1) {
        cEnd = cookieData.length;
      }
      return unescape(cookieData.substring(j+1, cEnd));
    }
    i++; 
  }
  return "";
}

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

Если у вас возникли затруднения с пониманием данного кода, то обратитесь к набору функций, написанных опытным программистом JavaScript и Web-дизайнером Биллом Дортчем (Bill Dortch) из hIdaho Design. Созданные им функции обеспечивают универсальный доступ к cookie-записям, которые можно использовать во всех страницах, управляемых cookie. В листинге представлены cookie-функции Билла; они содержат различные методы предотвращения ошибок в дате, которые могут появиться в некоторых версиях NN2. Вас может смутить большой объем этого листинга, однако не волнуйтесь: большинство строк — простые комментарии.

Листинг cookie-функции Билла Дортча

<html>
<head>
<title>Cookie Functions</title>
</head>
<body>
<script type="text/javascript"> 
<!-- начало сценария 
// 
// Соок1е_функции — "Night of the Living Cookie" версия (25.07.96)
//
// Написан: Биллом Дортчем, hIdaho Design < bdortch@hidaho.com >
// Следующие функции созданы для работы с существующим доменом
// Эта версия выполняет более "жесткое" удаление
// cookie-записей. Предыдущие версии устанавливали дату истечения
// срока службы записи на одну миллисекунду меньше, чем;
// текущее время однако, этот метод не работал в Netscape
// Navigator2.02 (хотя он работает как в более ранних, так и
// в более поздних версиях этого браузера), так как в
// результате его действия появлялись вечно живущие записи-
// "призраки", которые никак нельзя было удалить.
// Теперь DeleteCookie устанавливает в качестве даты истечения
// срока хранения записи самую раннюю дату, пригодную для
// использования (первая секунда 1970 года), и на всякий случай
// обнуляет ее значение.
// Также в этой версии функции DeleteCookie добавлены
// необязательные параметры пути и домена. Необходимо указывать
// путь и/или домен как при создании, так и при удалении
// cookie**, иначе запись не будет удалена.
// Функция FixCookieDate должна вызываться только для исправления
// ошибок в дате на Мае 2.x. Эта функция должна вызываться *один
// раз* после создания объекта Date и перед обращением к
// SetCookie. Поскольку ошибка в дате в Маc воздействует на все
// даты, а не только на те, которые передаются
// в функцию SetCookie, необходимо привыкнуть к тому, что
// придется вызывать FixCookieDate при создании каждого
// нового объекта:
//
// var theDate = new Date();
// FixCookieDate (theDate);
//
// Функция FixCookieDate не работает ни на одной платформе
// кроме Мае, поэтому нет необходимости определять платформу
// пользователя перед каждым ее вызовом.
// В этой версии также проведены некоторые незначительные
// усовершенствования.
// ** Заметьте, что можно устанавливать несколько записей с одним
// и тем же именем, но различными путями. Например:
//
// SetCookie ("color","red",null."/outer");
// SetCookie ("color","blue",null."/outer/inner");
//
// Однако GetCookie не может различить эти два выражения и
// возвращает значение первой найденной записи с данным именем.
// Поэтому рекомендуется *не* использовать одинаковые имена
// для cookie с разными путями. (Имейте в виду, что у каждой
// записи *всегда* есть свой путь; если он полностью не
// определен, используется путь, установленный документом.)
//
// История обновления:
//
// "Toss Your Cookies" версия (22.03.96)
// Добавление функции FixCookieDate() для исправления ошибок
// в дате в Мае
//
// "Second Helping" версия (21.01.96)
// Функция SetCookie() дополнена путем, доменом и параметрами
// безопасности
// "Самодельные" функции кодирования/декодирования заменены
// на новые
// функции escape и unescape в Netscape Navigator
//
// "Free Cookies " версия (декабрь 95)
// За более подробной информацией о значениях cookie параметров
// и о cookie пожалуйста, обращайтесь на официальный узел:
// http://www.netscape.com/newsref/std/cookie_spec.html
// *******************************************************
// "Внутренняя" функция для возвращения декодированного значения
//
function getCookieVal(offset) {
  var endstr = document.cookie.indexOf (";", offset); 
  if (endstr == -1) endstr = document.cookie.length;
  return unescape(document.cookie.substringtoffset, endstr));
}
//
// Эта функция необходима для исправления ошибок в дате на
// Мае 2.x. Вызывайте ее для проверки объекта даты перед
// отправкой данных в функцию SetCookie.
// ВНИМАНИЕ: Эта функция должна вызываться только *один раз*
// для каждого полученного объекта даты!
// См. пример в конце листинга.
//
function FixCookieDate(date) { 
  var base = new Date(0); 
  var skew = base.getTime(); // отсчет времени (Unix) должен начинаться с О 
  if (skew > 0) // везде кроме Mac
  date.setTime (date.getTime() — skew);
}
//
// Функция предназначена для возвращения значения cookie, 
// используя параметр "name".
// name — строковый объект, содержащий имя cookie. 
// returns — строковый объект, содержащий значение cookie либо 0, 
// если таковая запись не существует 
//
function GetCookie(name) { 
  var arg = name + "="; 
  var alen = arg.length; 
  var clen = document.cookie.length; 
  var i = 0;
  while (i < clen) { 
    var j = i + alen;
    if (document.cookie.substring(i, j) == arg) return getCookieVal(j); 
    i = document.cookie.indexOf(" ", 1) + 1; 
    if (i ==0) break;
  }
  return null;
}
//
// Функция для создания или обновления cookie.
// name — строковый объект, содержащий имя cookie.
// value — строковый объект, содержащий значение cookie.
// Может содержать любой действительный строковый символ.
// [expires] — объект Date, содержащий срок службы cookie.
// Если таковое отсутствует, удаляет cookie после завершения
// сеанса работы браузера.
// [path] — строковый объект, указывающий путь-нужной cookie
// записи. Если он не указан либо имеет нулевое значение,
// использует путь вызванного документа.
// [domen] — строковый объект, указывающий домен нужной
// cookie-записи. Если он не указан или имеет нулевое значение,
// используется домен вызванного документа.
// [secure] — булево значение (true / false), определяет
// необходимость использования безопасного соединения (HTTPS)
// Первые два параметра необходимы. Остальные, если указаны,
// то должны быть переданы в порядке, описанном выше. Для того
// чтобы зарезервировать поле, не используемое в данный момент,
// определите его как нулевое. Например, код для вызова функции
// SetCookie с заданными именем, значением и путем, имеет
// следующий вид:
// SetCookie ("myCookieName", "myCookieValue", null, "/");
//
// Заметьте, что перемещение опущенных параметров не требует
// метки—заполнителя. Чтобы установить безопасный cookie в
// "/myPath", срок службы которого заканчивается после
// текущего сеанса, необходимо использовать выражение:
//
// SetCookie (myCookieVar, cookieValueVar, null, /myPath", null, true); 
//
function SetCookie(name,value,expires,path,domain,secure) { 
  document.cookie = name + "-" + escape (value) + 
    ((expires) ? "; expires=+ expires.toGMTString() : "") + 
    ((path) ? "; path=" + path : "") + 
    ((domain) ? "; domain'" + domain : "") + 
    ((secure) ? "; secure" : "");
}
// Функция для удаления cookie. (Устанавливает дату истечения
// срока службы записи в начало)
// name — строковый объект, содержащий имя cookie.
// path — строковый объект, содержащий путь cookie-записи,
// которая впоследствии будет удалена. Это должен быть тот же
// путь, который использовался при создании cookie, или нулевое
// значение, если при создании записи путь не был определен.
// domen — строковый объект, который содержит домен cookie,
// предназначенный для удаления. Это должен быть тот же домен,
// который использовался для создания cookie, или нулевое
// значение, если при создании записи домен не был определен.
//
function DeleteCookie(name,path,domain) {
  if (GetCookie(name)) {
    document.cookie = name + "=" +
      ((path) ? "; path=" + path : "") + 
      ((domain) ? "; domain=" + domain : "") + 
      "; expires=Thu, Ol-Jan-70 00:00:01 GMT";
  } 
}
//
// Примеры:
//
var expdate = new Date();
FixCookieDate(expdate); // Исправляет ошибки в дате Mac, 
// используется только один раз для каждого объекта Date! 
expdate.setTime(expdate.getTime() + (24 * 60 * 60 * 1000)); 
// 24 от текущего времени 
SetCookie("ccpath", "http://www.hidaho.coin/colorcenter/", expdate); 
SetCookie("ccname", "hidaho Design ColorCenter", expdate); 
SetCookie("tempvar", "Это временная cookie."); 
SetCookie("ubiquitous","Эта cookie будет работать в любом месте данного домена",null,"/"); 
SetCookie("paranoid","Эта cookie обеспечивает безопасное соединение",expdate,"/",null,true); 
SetCookie("goner","Эта cookie должна быть удалена!"); 
document.write(document.cookie + "<br>"); 
DeleteCookie("goner");
document.write(document.cookie + "<br>");
document.write("ccpath = " + GetCookie("ccpath") + "<br>"); 
document.write("ccname — " + GetCookie("ccname") + "<br>"); 
document.write("tempvar = " + GetCookie("tempvar") + "<br>"); 
// конец сценария --> 
</script> 
</body> 
</html>

Дополнительные пакеты

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

Так как каждая cookie-запись сохраняет данные в виде обычного текста, можно создать собственные текстовые структуры данных для последующего размещения в них записей, состоящих из нескольких частей. (Но не следует забывать о практическом пределе: 2 000 символов на пару имя-значение в пределах максимума (4 000 символов для каждой объединенной cookie-записи домена).) Таким образом определяется символ-разделитель, который не используется ни в одной из cookie-записей. Например, в приложении Decision Helper (глава 55) для разделения нескольких целых чисел, сохраненных в cookie, используется точка.

В случае применения установленного символа-разделителя необходимо написать функции для сбора этих подчиненных "cookie" в одну строку, и наоборот, разделения ее на отдельные записи. Такой путь более длинный, но он обеспечивает постоянство сведений о клиенте.