- На адрес 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);
Надеюсь было полезно. В комментариях приветствуются варианты попроще.
ссылка на оригинал статьи https://habr.com/ru/post/508674/