Тема: Авторизация на сайтах
По просьбе разобраться с авторизацией torrent-tv (который в данный момент недоступен, да ещё и заблокирован на территории России), пояснения как её сделать в коде буду пояснять на примере сайта 1ttv.org
Чтобы реализовать авторизацию на сайтах в коде скрипта, нужно, для начала, просмотреть как она происходит на сайте в браузере.
Для этого можно использовать любой http сниффер, например Charles.
Просмотр авторизации через браузер
- Запускаем Charles
- Открываем браузер, идём на страницу сайта. Если нужно, предварительно нужно выйти (разлогиниться) на нём, чтобы перехватить момент именно успешной авторизации.
- Логинимся на сайте.
- Идём в сниффер и смотрим, как происходили запросы (Request) и какие сервер отдал после этого ответы (Response).
- Находим структуре запросов
http://1ttv.org
и смотрим, к каким страничкам были запросы.
Смотрим как прошёл запрос:
Видим, что запрос идёт на страничку /auth.php. Справа открываем вкладку "Request", для удобства внизу нажимаем вкладку "Raw" и смотрим все данные, которые были отосланы на сервер.
Тут видно, что запрос идёт методом POST, сверху видны HTTP заголовки (Accept
, Origin
, User-Agent
...), последней строкой переданы данные через этот метод. В данном примере переданы данные вида: email=<логин>&password=<пароль>&enter=%D0%92%D0%BE%D0%B9%D1%82%D0%B8
.
На стороне сервера также могут проверяться заголовки приходящих запросов, чтобы запросы шли только от их сайта или проверенных источников. Поэтому нам тоже нужно будет в коде скрипта установить несколько заголовков: "Referer: http://1ttv.org/auth.php", "Origin: http://1ttv.org". Как устанавливать и передавать HTTP заголовки будет показано в коде ниже.
Иногда также полезно устанавливать User-Agent и другие заголовки. Зависит от конкретного случая.
Переключаемся на вкладку Response, чтобы посмотреть ответ на данный запрос:
HTTP/1.1 302 Moved Temporarily
Date: Mon, 14 Dec 2015 08:57:16 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=60
Expires: Thu, 01 Jan 1970 00:00:01 GMT
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: PHPSESSID=92ec42ccac6d6d79c4e9f2323e55adf4; expires=Tue, 15-Dec-2015 08:57:15 GMT
Location: http://1ttv.org/cabinet.php
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block;
Server: ddos-guard.net
Тут первой строчкой видно, что в ответ приходит редирект (перенаправление) с кодом 302 Moved Temporarily. У ответов перенаправления с кодами 3xx должен быть установлен заголовок Location, в данном случае его значение: http://1ttv.org/cabinet.php
Тут же идёт установка значения Cookie. Вот это то значение нам и понадобится.
Надо сразу сказать, что тут простейший пример авторизации. В других случаях, нужно также брать не только cookie (и не обязательно их), но и другие установленные заголовки. Какие именно заголовки и значения проверяются на сервере, сразу сказать невозможно. Поэтому может потребоваться эксперименты с их установкой. В крайнем случае, вообще всё полностью повторять, как это происходит в браузере (устанавливать напрочь все заголовки).
Итого, мы посмотрели как происходит успешная авторизация через браузер и получили все данные для этого нужные.
Подготовка хитрого плана
Итак, у нас есть данные, которые нужно отправить в запросе на сервер. Нужно будет установить HTTP заголовки, как минимум:
Referer: http://1ttv.org/auth.php
, Origin: http://1ttv.org
.
Страница авторизации: /auth.php
. Метод запроса должен быть POST
и передать данные POST запроса.
Т.е. план такой:
- Сделать POST запрос на страничку http://1ttv.org/auth.php с переданными данными email=...&password=...&enter=%D0%92%D0%BE%D0%B9%D1%82%D0%B8
- Перехватить ответ перенаправления и получить HTTP заголовки ответа
- Взять оттуда значение cookie. Которое потом в следующих запросах в коде скрипта везде подставлять в Headers (в заголовки при запросе страниц сайта).
Сделать запрос мы можем функцией
HmsSendRequestEx('1ttv.org', '/auth.php', 'POST', 'application/x-www-form-urlencoded', sHeaders, sPost, 80, iFlags, sAnswHeaders, true);
Где переменные: sHeaders - будет содержать установленные заголовки ("Referer: ...", "Origin: ..."), sPost - данные POST запроса ("email=..."), iFlags - флаг настроек для избежания автоматического перенаправления (о нём в пояснениях ниже), sAnswHeaders - а в эту переменную будут получены заголовки ответа от сервера, где можно выудить интересную информацию - установку Cookies.
Вычленить нужное значение из заголовков ответа sAnswHeaders можно командой
HmsRegExMatch('(PHPSESSID=.*?);', sAnswHeaders, sCookie);
Которая, в случае успеха, по регулярному выражению найдёт в заголовках ответа sAnswHeaders значение PHPSESSID и занесёт его в переменную sCookie.
Написание кода авторизации
Для этого, я бы написал отдельную функцию, которую можно было бы вызывать, например, из главной процедуры.
Function Login1TTV(): Boolean;
Var
sName, sUser, sPass, sPost, sHeaders, sAnswHeaders, sCookie: String;
iFlags: Integer;
Begin
sHeaders := 'http://1ttv.org/auth.php'+#10#13+
'Origin: http://1ttv.org'+#10#13;
iFlags := $00200000; // INTERNET_FLAG_NO_AUTO_REDIRECT=0x00200000
sUser := HmsPercentEncode(HmsUtf8Encode(mpPodcastAuthorizationUserName));
sPass := HmsPercentEncode(HmsUtf8Encode(mpPodcastAuthorizationPassword));
sPost := 'email='+sUser+'&password='+sPass+'&enter=%D0%92%D0%BE%D0%B9%D1%82%D0%B8';
HmsSendRequestEx('1ttv.org', '/auth.php', 'POST', 'application/x-www-form-urlencoded', sHeaders, sPost, 80, iFlags, sAnswHeaders, true);
Result := HmsRegExMatch('(PHPSESSID=.*?);', sAnswHeaders, sCookie);
If Result Then
gsHeaders := gsHeaders+'Cookie: '+sCookie+#10#13
Else
HmsLogMessage(2, "Ошибка авторизации на 1ttv.org");
End;
А теперь пояснения к коду
Сначала объявляются переменные, которые будут использоваться в функции (всё что перечисляется после ключевого слова Var).
Var
sName, sUser, sPass, sPost, sHeaders, sAnswHeaders, sCookie: String;
iFlags: Integer;
Потом в переменную sHeaders устанавливаем HTTP заголовки, которые мы будем передавать при запросе. Заголовки разделяются знаками перевода строк и каретки - #10#13.
sHeaders := 'http://1ttv.org/auth.php'+#10#13+
'Origin: http://1ttv.org'+#10#13;
В переменную iFlags заносим значение флага INTERNET_FLAG_NO_AUTO_REDIRECT, чтобы ответ пришёл ДО перенаправления.
Указать метод запроса (POST) и получить заголовки ответа можно только функцией HmsSendRequestEx.
И ещё раз. Параметры функции HmsSendRequestEx:
- '1ttv.org' - сервер, куда отправляется запрос. Указывается без протокола (http и проч) и страниц.
- '/auth.php' - страница запроса. Указывается без сервера. Могут быть после знака '?' указывать параметры для GET запроса.
- 'POST' - метод запроса. (Могут быть 'GET', 'POST', 'HEAD', 'PUT', 'DELETE')
- 'application/x-www-form-urlencoded' - тип запрашиваемого контента. Может принимать значения MIME типа.
- sHeaders - Referer + могут быть указаны дополнительные HTTP заголовки, разделённые знаками #10#13 (0x0A 0x0D или '\r\n')
- 80 - порт. Обычно для http порт 80, для https - 443
- iFlags - интернет флаги. Список значений можно посмотреть здесь (если забыли ссылку, гуглить "internet flags")
- sAnswHeaders - переменная, которая будет содержать HTTP заголовки ответа. Очень для нас важный параметр.
- true - флаг, указывающий автоматически определять запакованные ответы и распаковывать их (например, если в заголовках было указана поддержка сжатия данных Accept-Encoding: gzip, deflate)
Обычно, при запросе страниц их перенаправление происходит автоматически и в ответе мы получаем уже данные после перенаправления. Поэтому, чтобы указать, чтобы ответ пришел без автоматического перенаправления, мы используем флаг INTERNET_FLAG_NO_AUTO_REDIRECT и указываем его значение в переменной iFlags.
Функция возвращает загруженные данные (страницы), но в данном случае нам это не нужно и мы тут анализируем только HTTP заголовки ответов, которые появляются в sAnswHeaders после запроса.
Оттуда мы командной
Result := HmsRegExMatch('(PHPSESSID=.*?);', sAnswHeaders, sCookie);
получаем значение cookie PHPSESSID и запоминаем в переменной sCookie.
Полученное значение sCookie мы добавляем к глобальной переменной gsHeaders, которая будет передаваться всем запросам для загрузки страниц в дальнейшем.
gsHeaders := gsHeaders+'Cookie: '+sCookie+#10#13
Значит, нам нужно в главной процедуре, установить значение глобальной gsHeaders. Если установлено имя пользователя в подкасте, сделать вызов нашей функции авторизации на сайте Login1TTV(), где, если всё успешно, к gsHeaders будут добавлены нужные заголовки (тут "Cookie: PHPSESSID=...") и в дальнейшем во всех функциях загрузки страниц мы можем указывать gsHeaders.
Для того, чтобы приведённый выше код работал в подкасте для 1ttv, нужно в коде скрипта по Alt+2 в самом вверху после ключевого слова Var объявить также глобальную переменную gsHeaders
var
gsHeaders: String;
А в главной процедуре можно сделать примерно так:
gsHeaders := 'Referer: '+mpFilePath+#10#13+
'Origin: http://1ttv.org'+#10#13+
'Accept-Encoding: gzip, deflate'+#10#13+
'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36'+#10#13;
If mpPodcastAuthorizationUserName<>'' Then Login1TTV();
sWebPage := HmsDownloadURL(mpFilePath, gsHeaders, True);
if (Pos('film_selector.php', sPage)>1) then
for i := 2 to 20 do sWebPage := sWebPage+HmsDownloadURL(mpFilePath+'&page='+IntToStr(i), gsHeaders, True);
sWebPage := HmsUtf8Decode(sWebPage);
sWebPage := HmsRemoveLineBreaks(sWebPage);
Заметьте, в функции HmsDownloadURL вторым параметром мы указываем глобальную переменную gsHeaders, которая, кроме установленных заголовков, после нашей функции Login1TTV()
будет содержать полученную и добавленную cookie, которая будет говорить сайту, что мы на самом деле авторизованные пользователи.
Что не понятно и глупые вопросы можно задавать тут.