«1. Обзор

В этой статье мы собираемся исследовать низкоуровневые операции с сетевым программированием на Java. Мы более подробно рассмотрим файлы cookie.

Платформа Java поставляется со встроенной сетевой поддержкой, включенной в пакет java.net:

import java.net.*;

2. Файлы cookie HTTP

Всякий раз, когда клиент отправляет HTTP-запрос на сервер и получает ответ для него сервер забывает об этом клиенте. В следующий раз, когда клиент запросит снова, он будет рассматриваться как совершенно новый клиент.

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

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

Основным классом в пакете java.net для обработки файлов cookie является CookieHandler. Существуют и другие вспомогательные классы и интерфейсы, такие как CookieManager, CookiePolicy, CookieStore и HttpCookie.

3. Класс CookieHandler

Рассмотрим этот сценарий; мы общаемся с сервером по адресу http://baeldung.com или любому другому URL-адресу, который использует протокол HTTP, объект URL будет использовать механизм, называемый обработчиком протокола HTTP.

Этот обработчик протокола HTTP проверяет, существует ли в системе экземпляр CookieHandler по умолчанию. Если есть, он призывает его взять на себя управление состоянием.

Таким образом, класс CookieHandler предназначен для обеспечения механизма обратного вызова в интересах обработчика протокола HTTP.

CookieHandler — это абстрактный класс. У него есть статический метод getDefault(), который можно вызвать для получения текущей установки CookieHandler, или мы можем вызвать setDefault(CookieHandler), чтобы установить свою собственную. Обратите внимание, что вызов setDefault устанавливает объект CookieHandler для всей системы.

Он также имеет put(uri, responseHeaders) для сохранения любых файлов cookie в хранилище файлов cookie. Эти файлы cookie извлекаются из заголовков ответа HTTP от данного URI. Он вызывается каждый раз при получении ответа.

Связанный метод API — get(uri,requestHeaders) извлекает файлы cookie, сохраненные под заданным URI, и добавляет их в requetHeaders. Он вызывается непосредственно перед выполнением запроса.

Все эти методы должны быть реализованы в конкретном классе CookieHandler. На данный момент нашего внимания заслуживает класс CookieManager. Этот класс предлагает полную реализацию класса CookieHandler для наиболее распространенных случаев использования.

В следующих двух разделах мы собираемся изучить класс CookieManager; сначала в режиме по умолчанию, а затем в пользовательском режиме.

4. CookieManager по умолчанию

Чтобы иметь полную структуру управления файлами cookie, нам нужны реализации CookiePolicy и CookieStore.

CookiePolicy устанавливает правила принятия и отклонения файлов cookie. Конечно, мы можем изменить эти правила в соответствии с нашими потребностями.

Далее — CookieStore делает именно то, что предполагает его название, у него есть методы для сохранения и извлечения файлов cookie. Естественно, мы можем настроить механизм хранения и здесь, если нам нужно.

Давайте сначала посмотрим на значения по умолчанию. Чтобы создать CookieHandler по умолчанию и установить его как значение по умолчанию для всей системы:

CookieManager cm = new CookieManager();
CookieHandler.setDefault(cm);

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

Когда дело доходит до CookiePolicy, реализация по умолчанию — CookiePolicy.ACCEPT_ORIGINAL_SERVER. Это означает, что если ответ получен через прокси-сервер, то cookie будет отклонен.

5. Пользовательский CookieManager

Теперь давайте настроим CookieManager по умолчанию, предоставив наш собственный экземпляр CookiePolicy или CookieStore (или обоих).

5.1. CookiePolicy

CookiePolicy предоставляет несколько предопределенных реализаций для удобства:

    «CookiePolicy.ACCEPT_ORIGINAL_SERVER — могут быть сохранены только файлы cookie с исходного сервера (реализация по умолчанию) CookiePolicy.ACCEPT_ALL — могут быть сохранены все файлы cookie независимо от их происхождения CookiePolicy.ACCEPT_NONE — файлы cookie не могут быть сохранены

В просто измените текущую CookiePolicy, не реализуя нашу собственную, мы вызываем setCookiePolicy для экземпляра CookieManager:

CookieManager cm=new CookieManager();
cm.setCookiePolicy(CookiePolicy.ACCEPT_ALL);

Но мы можем сделать гораздо больше настроек, чем это. Зная поведение CookiePolicy.ACCEPT_ORIGINAL_SERVER, давайте предположим, что мы доверяем определенному прокси-серверу и хотели бы принимать файлы cookie от него поверх исходного сервера.

Нам нужно реализовать интерфейс CookiePolicy и реализовать метод shouldAccept; именно здесь мы изменим правило приема, добавив доменное имя выбранного прокси-сервера.

Назовем новую политику ProxyAcceptCookiePolicy. По сути, он отклонит любой другой прокси-сервер из своей реализации shouldAccept, кроме заданного прокси-адреса, а затем вызовет метод shouldAccept объекта CookiePolicy.ACCEPT_ORIGINAL_SERVER для завершения реализации:

public class ProxyAcceptCookiePolicy implements CookiePolicy {
    private String acceptedProxy;

    public boolean shouldAccept(URI uri, HttpCookie cookie) {
        String host = InetAddress.getByName(uri.getHost())
          .getCanonicalHostName();
        if (HttpCookie.domainMatches(acceptedProxy, host)) {
            return true;
        }

        return CookiePolicy.ACCEPT_ORIGINAL_SERVER
          .shouldAccept(uri, cookie);
    }

    // standard constructors
}

Когда мы создаем экземпляр ProxyAcceptCookiePolicy, мы передаем в строке адреса домена, с которого мы хотели бы принимать файлы cookie в дополнение к исходному серверу.

Затем мы устанавливаем этот экземпляр в качестве политики cookie для экземпляра CookieManager, прежде чем установить его как CookieHandler по умолчанию:

CookieManager cm = new CookieManager();
cm.setCookiePolicy(new ProxyAcceptCookiePolicy("baeldung.com"));
CookieHandler.setDefault(cm);

Таким образом, обработчик cookie будет принимать все файлы cookie с исходного сервера, а также с http:/ /www.baeldung.com.

5.2. CookieStore

CookieManager добавляет файлы cookie в CookieStore для каждого HTTP-ответа и извлекает файлы cookie из CookieStore для каждого HTTP-запроса.

Реализация CookieStore по умолчанию не имеет сохранения, она скорее теряет все свои данные при перезапуске JVM. Больше похоже на оперативную память в компьютере.

Итак, если мы хотим, чтобы наша реализация CookieStore вел себя как жесткий диск и сохранял файлы cookie при перезапусках JVM, мы должны настроить его механизм хранения и извлечения.

Следует отметить, что мы не можем передать экземпляр CookieStore в CookieManager после создания. Наш единственный вариант — передать его во время создания CookieManager или получить ссылку на экземпляр по умолчанию, вызвав new CookieManager().getCookieStore() и дополнив его поведение.

Вот реализация PersistentCookieStore:

public class PersistentCookieStore implements CookieStore, Runnable {
    private CookieStore store;

    public PersistentCookieStore() {
        store = new CookieManager().getCookieStore();
        // deserialize cookies into store
        Runtime.getRuntime().addShutdownHook(new Thread(this));
    }

    @Override
    public void run() {
        // serialize cookies to persistent storage
    }

    @Override
    public void add(URI uri, HttpCookie cookie) {
        store.add(uri, cookie);

    }
    
    // delegate all implementations to store object like above
}

Обратите внимание, что мы получили ссылку на реализацию по умолчанию в конструкторе.

Мы реализуем runnable, чтобы можно было добавить хук выключения, который запускается при завершении работы JVM. Внутри метода run мы сохраняем все наши файлы cookie в памяти.

Мы можем сериализовать данные в файл или любое подходящее хранилище. Обратите также внимание, что внутри конструктора мы сначала считываем все файлы cookie из постоянной памяти в CookieStore. Эти две простые функции делают хранилище CookieStore по умолчанию практически постоянным (в упрощенном виде).

6. Заключение

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

Полный исходный код статьи и все фрагменты кода можно найти в проекте GitHub.