Допустим, что нам надо подключиться к серверу, авторизоваться и поддерживать сессию. В браузере это выглядит следующим образом:
Рассмотрим, как это можно реализовать на Java и на Python.
При выборе библиотеки для работы с сетью на Python большинство сайтов будет вам рекомендовать библиотеку requests , которая полностью оправдывает свой лозунг:
Заметим, что махинации с Cookie и переадресацией происходят под капотом, прямо как в браузере. Так же можно отметить, что если завести ещё одну переменную session2, то можно держать активными сразу два подключения.
Поиски библиотеки для работы с сетью на Java приводят сразу к нескольким библиотекам.
Например, java.net, Apache HttpClient и OkHttp3.
Я остановился на HttpURLConnection (java.net). Плюсами данной библиотеки является то, что это библиотека "из-под коробки", а так же, если надо написать приложение под android, на официальном сайте есть документация. Минусом является очень большой объём кода. (После Python это просто боль).
Итак, начнём. По документации для работы с сессиями можно использовать CookieManager:
Что нужно отметить, используя такой подход:
Учитывая это, можно записать и в одну строчку:
Пункт 1 и 2. Выполним GET запрос для получения первой Cookie:
После этого наш cookieManager будет содержать Cookie с сервера и автоматически подставит её в следующий запрос.
Веселье начинается с POST запросом.
Нужно записать в Headers «Content-Type: application/x-www-form-urlencoded».
Почему метод называется setRequestProperty, а не setHeaders (или addHeaders) при наличии метода getHeaderField, остаётся загадкой.
Далее идёт код, который непонятно по каким причинам не засунут под капот библиотеки.
Нужна эта строчка кода для открытия исходящего потока. Забавно, что без этой строки мы получим следующее сообщение:
Остаётся считать ответ с уже перенаправленного запроса.
Можно реализовать и без CookieManager и самому контролировать перемещение cookie.
Пункт 1 и 2. Вынимаем cookie.
Далее отправляем POST запрос, только на этот раз вставив cookie и отключив автоматическое перенаправление, т.к. перед ним надо успеть вытащить новое cookie:
Далее во все запросы просто добавляем следующую строку:
Надеюсь, было полезно. В комментариях приветствуются варианты попроще.
- На адрес http://localhost:8080/login отправляется пустой GET запрос.
- Сервер присылает формочку для заполнения логина и пароля, а также присылает Cookie вида «JSESSIONID=094BC0A489335CF8EE58C8E7846FE49B».
- Заполнив логин и пароль, на сервер отправляется POST запрос с полученной ранее Cookie, со строкой в выходном потоке «username=Fox&password=123». В Headers дополнительно указывается «Content-Type: application/x-www-form-urlencoded».
- В ответ сервер нам присылает новую cookie c новым «JSESSIONID=». Сразу же происходит переадресация на http://localhost:8080/ путём GET запроса с новой Cookie.
- Далее можно спокойно использовать остальное API сервера, передавая последнее Cookie в каждом запросе.
Рассмотрим, как это можно реализовать на Java и на Python.
Содержание
- Реализация на Python. Requests.
- Реализация на Java, HttpURLConnection и CookieManager.
- Реализация на Java, HttpURLConnection без CookieManager.
Реализация на 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);
Надеюсь, было полезно. В комментариях приветствуются варианты попроще.