Подключение к session в Java и Python. HttpURLConnection и CookieManager (Java). Requests(Python)

  • Tutorial
Допустим, что нам надо подключиться к серверу, авторизоваться и поддерживать сессию. В браузере это выглядит следующим образом:

  1. На адрес http://localhost:8080/login отправляется пустой GET запрос.
  2. Сервер присылает формочку для заполнения логина и пароля, а также присылает Cookie вида «JSESSIONID=094BC0A489335CF8EE58C8E7846FE49B».
  3. Заполнив логин и пароль, на сервер отправляется POST запрос с полученной ранее Cookie, со строкой в выходном потоке «username=Fox&password=123». В Headers дополнительно указывается «Content-Type: application/x-www-form-urlencoded».
  4. В ответ сервер нам присылает новую cookie c новым «JSESSIONID=». Сразу же происходит переадресация на http://localhost:8080/ путём GET запроса с новой Cookie.
  5. Далее можно спокойно использовать остальное API сервера, передавая последнее Cookie в каждом запросе.

Рассмотрим, как это можно реализовать на Java и на Python.



Содержание



Реализация на Python. Requests


При выборе библиотеки для работы с сетью на Python большинство сайтов будет вам рекомендовать библиотеку requests , которая полностью оправдывает свой лозунг:
HTTP for Humans
Вся задача решается следующим скриптом:

import requests
session = requests.session()  #создаём сессию
url = "http://localhost:8080/login"
session.get(url)   #получаем cookie
data = {"username": "Fox", "password": "123"} 
response = session.post(url, data=data) #логинимся

Заметим, что махинации с Cookie и переадресацией происходят под капотом, прямо как в браузере. Так же можно отметить, что если завести ещё одну переменную session2, то можно держать активными сразу два подключения.

Реализация на Java, HttpURLConnection и CookieManager


Поиски библиотеки для работы с сетью на Java приводят сразу к нескольким библиотекам.

Например, java.net, Apache HttpClient и OkHttp3.

Я остановился на HttpURLConnection (java.net). Плюсами данной библиотеки является то, что это библиотека "из-под коробки", а так же, если надо написать приложение под android, на официальном сайте есть документация. Минусом является очень большой объём кода. (После Python это просто боль).

Итак, начнём. По документации для работы с сессиями можно использовать CookieManager:

CookieManager cookieManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(cookieManager);

Что нужно отметить, используя такой подход:

  • «CookiePolicy.ACCEPT_ALL» указывает, что надо работать со всеми cookie.
  • Переменная cookieManager далее нигде не будет использоваться. Она контролирует все подключения, и, если необходимо поддерживать несколько активных сессий, необходимо будет в этой одной переменной руками менять Cookie

Учитывая это, можно записать и в одну строчку:

 CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));

Пункт 1 и 2. Выполним GET запрос для получения первой Cookie:

URL url = new URL("http://localhost:8080/login");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
final StringBuilder content = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
    content.append(inputLine);
}

После этого наш cookieManager будет содержать Cookie с сервера и автоматически подставит её в следующий запрос.

Веселье начинается с POST запросом.

url = new URL("http://localhost:8080/login");
con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");

Нужно записать в Headers «Content-Type: application/x-www-form-urlencoded».

Почему метод называется setRequestProperty, а не setHeaders (или addHeaders) при наличии метода getHeaderField, остаётся загадкой.

con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

Далее идёт код, который непонятно по каким причинам не засунут под капот библиотеки.

con.setDoOutput(true);

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

Exception in thread «main» java.net.ProtocolException: cannot write to a URLConnection if doOutput=false — call setDoOutput(true)
Открываем исходящий поток и записываем туда логин и пароль:

final DataOutputStream out = new DataOutputStream(con.getOutputStream());
out.writeBytes("username=Fox&password=123");
out.flush();
out.close();

Остаётся считать ответ с уже перенаправленного запроса.

Реализация на Java, HttpURLConnection без CookieManager


Можно реализовать и без CookieManager и самому контролировать перемещение cookie.
Пункт 1 и 2. Вынимаем cookie.

URL url = new URL("http://localhost:8080/login");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
final StringBuilder content = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
    content.append(inputLine);
String cookie = con.getHeaderField("Set-Cookie").split(";")[0];
}

Далее отправляем POST запрос, только на этот раз вставив cookie и отключив автоматическое перенаправление, т.к. перед ним надо успеть вытащить новое cookie:

// создаём запрос
url = new URL("http://localhost:8080/login");
con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
//указываем headers и cookie
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
con.setRequestProperty("Cookie", cookie);
//отключаем переадресацию
con.setInstanceFollowRedirects(false);
//отправляем логин и пароль
con.setDoOutput(true);
final DataOutputStream out = new DataOutputStream(con.getOutputStream());
out.writeBytes("username=Fox&password=123");
out.flush();
out.close();
//считываем и получаем второе cookie
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
final StringBuilder content = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
    content.append(inputLine);
String cookie2 = con.getHeaderField("Set-Cookie").split(";")[0];

Далее во все запросы просто добавляем следующую строку:

con.setRequestProperty("Cookie", cookie2);

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

Похожие публикации

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 8

    0

    "Плюсами данной библиотеки является то, что это библиотека "из-под коробки""
    Из под какой именно коробки, уточните, пожалуйста

      0
      Не нужно ничего дополнительно к проекту подключать.
        0

        Не 'из под коробки', а 'из коробки'.


        "Плюсами данной библиотеки является то, что это библиотека "из-под коробки", а так же, если надо написать приложение под android, на официальном сайте есть документация"


        Очень странные аргументы. Для python взяли стороннюю либу, а тут вдруг стандартную.
        Про андроид зачем вообще?
        Возьмите нормальную либу на Java, да сравните


        Upd: я имею в виду, что HttpUrlConnection более низкоуровневая библиотека, некорректно её сравнивать с requests, в плане читабельности. Моё имхо, с Java не работаю

          0
          Цель была подключиться к сессии. Я её решил. И на Python решение любого вопроса гуглиться довольно быстро. Попробуйте сначала определиться какую библиотеку использовать на java прежде чем решить этот кейс. У вас уйдёт не мало времени, чтобы выбрать подходящий вам вариант, потом разобраться как там что работает, а уж потом решите задачу.
            0

            Apache HttpComponents Client гуглится за одну минуту. Давайте всё–таки сравнивать корректно. Конкретно вот здесь (https://hc.apache.org/httpcomponents-client-ga/tutorial/html/statemgmt.html) расписано, как корректно работать с куками. Берёте HttpUrlConnection в Java — сравнивайте с питоновским http.client. И внезапно окажется, что по геморройности они вполне сопоставимы. Ну и что, что в экосистеме Java у вас есть на выбор несколько либ со схожим функционалом? У них, на минуточку, разные парадигмы — выбирайте то, что вам ближе. Тоже мне, бином Ньютона… а в экосистеме Scala есть выбор между Cats, Scalaz и ZIO, это как минимум… В Scala всё плохо с функциональным программированием из–за этого?

              0
              Цель была подключиться к сессии. Я её решил. И на Python решение любого вопроса гуглиться довольно быстро.

              Прям таки любого?

              Попробуйте сначала определиться какую библиотеку использовать на java прежде чем решить этот кейс

              Озвучьте требования и цель вначале. В данном случае, цель — подключиться в сессии, но библиотеку, внезапно, выбрали потому что 'из коробки' и 'есть документация для андроид'. Профессионально.

        +2
        В Java11 есть отличный HttpClient
        я бы использовал его
          0
          Я как-то делал сохранение куки из браузера JavaFX. Это было больно, но я сделал это.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое