Поиск по блогу

вторник, 26 мая 2009 г.

Пример авторизации на сайте с помощью idHTTP.Post

Несмотря на то, что все мои заготовки статей канули в Лету вместе с другими данными (в том числе исходниками), которые были на флешке, я не пала духом и попытаюсь написать что-нибудь полезное заново.

Сегодня расскажу, как использовать idHTTP.Post для авторизации на сайте. Я возьму для примера сайт LiveJournal.com.

Немного теории для начинающих. Итак, вызов метода Post компонента idHTTP отличается от вызова Get-а только тем, что помимо URL-а необходимо передать параметры. Параметры можно передавать в виде StringList-а, или каких-нибудь Stream-ов, или чего-нибудь еще подходящего.)

Пример Post-процедуры (параметры передаются в виде StringList-а):

procedure TForm1.Button1Click(Sender: TObject);
var
LoginInfo: TStringList;
Response: TStringStream;
begin
try
LoginInfo := TStringList.Create;
Response := TStringStream.Create('');
LoginInfo.Add('username=MyName');
LoginInfo.Add('password=MyPass');
IdHTTP1.Post('http://mywebsite.xxx/login.php',LoginInfo,Response);
Showmessage(Response.DataString);
finally
begin
Response.Free;
LoginInfo.Free;
end;
end;
end;


Пример Post-функции (параметры передаются в виде IdMultiPartFormDataStream-а):

uses IdMultipartFormData;
{ .... }

procedure TForm1.Button1Click(Sender: TObject);
var
data: TIdMultiPartFormDataStream;
begin
data := TIdMultiPartFormDataStream.Create;
try
// добавляем нужные параметры
data.AddFormField('param1', 'value1');
data.AddFormField('param2', 'value2');
// для примера выводим в мемо все, что вернулось
Memo1.Lines.Text := IdHTTP1.Post('http://localhost/script.php', data);
finally
data.Free;
end;
end;


Сейчас попробуем применить полученные знания. Идем на LiveJournal.com, включаем сниффер, логинимся на сайте и смотрим, какие параметры надо передавать ('mode=login', 'user=логин', 'password=пароль'). Авторизация не произойдет, если на стороне клиента не будут сохранены кукисы. Для сохранения кукисов среди компонентов Indy существует TidCookieManager. IdCookieManager подключается к idHTTP через свойство CookieManager.
idHttp.CookieManager := IdCookieManager;

В этом случае при запросах в заголовок добавляются кукисы, автоматически сохраненные в IdCookieManager. IdCookieManager можно найти на закладке Indy Misc или создать динамически.
idCookieManager

Поместим на форму 2 TEdit-а, TMemo и кнопку, на которую повесим следующий работающий код авторизации:

procedure TForm1.Button1Click(Sender: TObject);
var
Http : TidHttp;
CM : TidCookieManager;
Data : TStringList;
StrPage, UserID, UserName : String;
i : integer;
begin
try
Http := TIdHTTP.Create(Self);
Data := TStringList.Create;
CM := TidCookieManager.Create(Http);
Http.AllowCookies := true;
Http.CookieManager := CM;
Http.HandleRedirects := true;

Http.Request.Host:='livejournal.com';
Http.Request.UserAgent:='Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.0.10) Gecko/2009042316 Firefox/3.0.10';
Http.Request.Accept:='text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
Http.Request.AcceptLanguage:='ru,en-us;q=0.7,en;q=0.3';
Http.Request.AcceptCharSet:='windows-1251,utf-8;q=0.7,*;q=0.7';
Http.Request.Referer:='http://www.livejournal.com/';

Data.Add('mode=login');
Data.Add('user=' + Edit1.Text);
Data.Add('password=' + Edit2.Text);
StrPage := Http.Post('http://www.livejournal.com/login.bml?ret=1', Data);
finally
Data.Free;
CM.Free;
Http.Free;
end;

if Pos('<input class="logoutlj_hidden" id="user" name="user" type="hidden" value="'+Edit1.Text,StrPage) <> 0 then
ShowMessage('Авторизация прошла успешно')
else
ShowMessage('Авторизация провалилась');

Memo1.Lines.Text := StrPage;
end;


Возвращенные заголовки (после ответа сервера) можно посмотреть так:
idHttp.Response.RawHeaders.GetText;


Сохраненные в CookieManager-е кукисы можно посмотреть так:
for i := 0 to Http.CookieManager.CookieCollection.Count - 1 do
StrPage := StrPage + CM.CookieCollection.Items[i].CookieText + #13#10;


Вот что записал туда LiveJourmal.com:
Cookies LJ
Да, ЖЖ, мы тоже love you a lot :)
___

В качестве отступления от темы статьи делюсь ссылкой на сайт о программировании на Delphi для начинающих и не только.
___

Чтобы быть в курсе обновлений блога, можно подписаться на RSS.

Статьи схожей тематики:



35 комментариев:

  1. Сугубо положительная статья. Большое спасибо. Давно искал что-то похожее.
    Один вопрос: вот с ЖЖ вы указали, какие параметры передавать. А как с другими сайтами? Я не очень понял, какого рода "сниффер" здесь требуется. Можете что-нибудь посоветовать?

    ОтветитьУдалить
  2. я думаю автор имеет в виду HTTP Analizer

    ОтветитьУдалить
  3. а как быть , если нужно еще передавать id сессии. Не ...как передавать эту id я понял...А вот как получить ее(его...id в общем) не загружая страницыи не парся исходник html?

    ОтветитьУдалить
  4. Подскажите пожалуйста, а как можно отправить куки на сервер?

    ОтветитьУдалить
  5. СПАСИБО ОГРОМНОЕ!

    ОтветитьУдалить
  6. А не сталкивали ли Вы с такой вот проблемой:
    В длинном цикле идет обращение к скрипту php через IdHTTP.Post
    и когда цикл переваливает через значения 3900, то выскакивает ошибка:

    "Socket Error # 10048 Address already in use"

    Как это побороть?
    Вот кодинг:

    http := TIdHTTP.Create(nil);

    начало цикла:

    if http.Connected then http.Disconnect;
    Memo1.Lines.Append(http.Post('http://localhost/integrator.php', formData));
    formData.Free;
    if http.Connected then http.Disconnect;

    конец цикла.

    http.Free;

    ОтветитьУдалить
  7. Я ни с чем таким не сталкивалась. А что, если попробовать создавать idHTTP в начале и в конце внутри цикла? ) Говорю наугад, может, со "свеженьким" объектом каждый раз будет работать лучше? Если, конечно, это не идет вразрез с алгоритмом.)

    ОтветитьУдалить
  8. Маша, большое спасибо за статьи. Я по ним научился работать с интернетом. А статьи по созданию многопоточных интернет-приложений будут?

    ОтветитьУдалить
  9. исходник хороший вопросов нет но как быть с передаче руского текста в кодировке win-1251 да еще +xml + openssl с етим всюголову свернул весь нет перекопал

    ОтветитьУдалить
  10. Статья хороша....но есть вопрос
    Авторизация на сайте вконтакте.ру проходит успешно, но при попытке загрузить любую другую страницу этого сайта, то перикидывает на стр. входа т.к. идет неправильная обработка кукис, как я понял...
    немогли бы вы помочь с решением данной проблемы?

    ОтветитьУдалить
  11. Все эту проблему решил...
    написал свой обработчик куков

    ОтветитьУдалить
  12. Очень полезная статья. Вот правда не получилось просмотреть куки. Выдает ошибку. Но это не столь важно. Сейчас столкнулся с проблемой авторизации на сайте на движке ucoz. Как на нем можно авторизоваться при помощи idhttp? Интересующая нас строка с сниффере насколько понял эта:user=мыло&password=пароль&rem=1&a=2&ajax=1&rnd=863HTTP/1.1 200 OK

    ОтветитьУдалить
  13. Не знаю, пробовали ли вы авторизоваться на ЖЖ при помощи, приведённого куска кода, у меня лично не получилось.

    Поискал ответ в гугле и вот что я нашёл. http://jenyay.net/Programming/LJServer2#auth

    Оказывается не всё так просто, и для ЖЖ надо указывать схему авторизации а также шифровать данные функцией MD5

    ОтветитьУдалить
  14. Полиморфыч, если бы я не пробовала - я бы не написала. На момент написания все отлично работало.

    ОтветитьУдалить
  15. var
    Http : TidHttp;
    CM : TidCookieManager;
    Data : TStringList;
    StrPage, UserID, UserName : String;
    i : integer;
    begin
    try
    Http := TIdHTTP.Create(Self);
    Data := TStringList.Create;
    CM := TIdCookieManager.Create(Http);
    Http.AllowCookies := true;
    Http.CookieManager := CM;
    Http.HandleRedirects := true;
    Http.Request.Host:='connect.ua';
    Http.Request.Connection:='keep-alive';
    Http.Request.UserAgent:='Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.0.10) Gecko/2009042316 Firefox/3.0.10';
    Http.Request.Referer:='http://connect.ua/';
    Http.Request.ContentLength:=70;
    Http.Request.CacheControl:='max-age=0';
    Http.Request.ContentType:='application/x-www-form-urlencoded';
    Http.Request.Accept:='application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5';
    Http.Request.AcceptEncoding:='gzip,deflate,sdch' ;
    Http.Request.AcceptLanguage:='uk-UA,uk;q=0.8,ru;q=0.6,en-US;q=0.4,en;q=0.2';
    Http.Request.AcceptCharSet:='windows-1251,utf-8;q=0.7,*;q=0.3';

    Data.Add('submit=1');
    Data.Add('data[email]=' + Edit1.Text);
    Data.Add('data[password]=' + Edit2.Text);
    StrPage := Http.Post('http://connect.ua/login', Data);
    for i := 0 to Http.CookieManager.CookieCollection.Count - 1 do
    StrPage :=CM.CookieCollection.Items[i].CookieText + #13#10;
    finally
    Data.Free;
    CM.Free;
    Http.Free;
    end;

    Memo1.Lines.Text := StrPage;
    end;

    Що здесь не так ? Плизз!

    ОтветитьУдалить
  16. Маша, спасибо огромное. Написано кратко и по делу. Авторизация на ЖЖ работает без каких-либо переделок.

    ОтветитьУдалить
  17. У меня следующая проблема.
    На странице, которую передаю в Post, есть перенаправление на другую страницу. После перенаправления происходит отключение. С чем это может быть связано?

    ОтветитьУдалить
  18. С ходу сказать сложно, надо смотреть код.

    ОтветитьУдалить
  19. Доброго времени суток.

    Маша, тема, которую вы затронули в этом обзоре очень актуальна и мало где можно найти действительно толковое объяснение как у вас.

    Код, который вы написали, работает исправно и на сегодняшний день, не знаю на что там жаловались до меня. Правда возникло несколько вопросов:

    1) Как определить какие именно данные отправляются при регистрации? Было сказано, что вы использовали сниффер, а какой именно если не секрет?
    P.S. установил Iris Network Traffic Analyzer, но как-то слишком непонятно там что к чему.

    2) При запуске программы, в StrPage сохраняется код возвращаемой странички - это и есть залогиненая страничка. НО! как её открыть, скажем, в помещенном на форме WebBrowser-е?

    ОтветитьУдалить
  20. Влад, чтобы посмотреть хэдеры запросов я обычно использую плагин к FF, о котором писала здесь: http://parsing-and-i.blogspot.com/2009/04/http.html

    Код странички, возвращаемой после авторизации, я использую просто для того, чтобы проверить, прошла ли она успешно. Дальше его использовать не надо, тем более не надо загружать в TWebBrowser. Все операции, которые вам необходимы, совершайте через idHTTP с использованием нужных заголовков и кукисов.

    Если вы хотите работать с TWebBrowser - то это уже совсем другая история :)

    ОтветитьУдалить
  21. Спасибо, плагин отличный, очень удобно им пользоваться.

    Правда на другой сайт, сколько ни мучился, у меня ,пока что, так и не получается залогиниться :)

    А сайт, на который я пытаюсь залогиниться есть ни что иное как браузерная игрушка http://heroeswm.ru . Да да, а кому охота целый день просиживать за компьютером кликая туда сюда ))

    Хотел скачать для ознакомления ваши исходники к игре Minicity, но ссылка оказалась не рабочей (Вот обзор с ссылкой на исходники: http://parsing-and-i.blogspot.com/2008/12/blog-post.html).

    Не совсем понимаю в чем заключается проблема моего “творения”, если вы подскажете в каком направлении надо двигаться, надеюсь, у меня получится залогиниться.

    Для начала порядок подключения:
    1) Заполнить input с логином
    2) Заполнить input с паролем
    3) Нажать кнопку “Войти”. Но эта “кнопка ” не есть Button, а является полем input type=image name=lbut src='i/index/left_buttons21.jpg' title='Войти в игру!'. И при POST запросе сюда передаются переменные lbut.x и lbut.y – это, как я понял есть координаты картинки. Как их вычислить при POST запросе через IdHttp я не понял, и не знаю имеют ли они вообще критическое значение, так как эти координаты меняются, если например в браузере открываю боковую панель.

    Проблема следующая: после отправки POST запроса с заполненными переменными на страничку http://www.heroeswm.ru/login.php :

    Data.Add('LOGIN_redirect=1');
    Data.Add('login=' + Edit1.Text);
    Data.Add('lreseted=1');
    Data.Add('pass=' + Edit2.Text);
    Data.Add('preseted=0');
    Data.Add('lbut.x=74'); // Координаты изображения заполняю “наобум”
    Data.Add('lbut.y=5');

    приходит перенаправление на “домашнюю” страницу персонажа http://www.heroeswm.ru/home.php :

    HTTP/1.1 302 Found
    Server: nginx
    Date: Sat, 03 Jul 2010 11:18:45 GMT
    Content-Type: text/html
    Transfer-Encoding: chunked
    Connection: keep-alive
    Set-Cookie: sid=deleted; expires=Fri, 03-Jul-2009 11:18:44 GMT
    Set-Cookie: pl_id=deleted; expires=Fri, 03-Jul-2009 11:18:44 GMT
    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    Pragma: no-cache
    Set-Cookie: PHPSESSID=bd9ed3c4cac4b91ac27e2cf61c3f6f30; path=/; HttpOnly
    Set-Cookie: pl_id=460703
    Set-Cookie: sid=cbc7601ea52e771cad748cbb2bd55b17
    Location: home.php

    так как это перенаправление приходит всегда, то сразу после POST запроса открываю перенаправленную страничку следующим образом: Http.Get(Http.Response.Location). Но вот незадача, при попытке получить эту страничку я получаю не залогиненую страничку, а исходную страницу сайта http://heroeswm.ru , как будто программа и не отправляла POST Запрос.

    Вот исходник моего чудо творения, если необходим: http://file.qip.ru/file/133760312/21e99199/Heroes_Connector.html

    ОтветитьУдалить
  22. Влад, сейчас у меня, к сожалению, нет времени регистрироваться на том сайте и тестировать ваш код. Что могу посоветовать? Проверьте куки. CookieManager в непоследних версиях инди может глючить, помочь может или обновление инди, или разбор кукисов "вручную".

    ОтветитьУдалить
  23. Маша, вы правы, проблема скорее всего в кукисах. Посмотрел содержимое CookieManager-а, там полный хаос, одни и те же записи по 2-3 раза встреечаются + не отправляются нужные при POST запросе.

    Не подскажите как вручную можно очистить содержимое CookieManager-а и как добавить к отправляемым заголовкам нужные куки?

    ОтветитьУдалить
  24. Влад, в двух строках не ответить. Поищите в интернете, на программерских форумах эта тема поднималась множество раз. Или просто обновите Indy :)

    ОтветитьУдалить
  25. Большое спасибо, Мария! Информация оказалась крайне полезна.

    ОтветитьУдалить
  26. Не сохраняет все куки, только половину

    ОтветитьУдалить
  27. Http := TIdHTTP.Create(Self);
    в этой строке что означает:
    Self
    я пишу на c++

    ОтветитьУдалить
  28. Анонимный, в скобках в Delphi передается родитель. В с++, вроде, ничего не надо указывать, там для создания объекта другой синтаксис, если я ничего не путаю.

    ОтветитьУдалить
  29. Project proj.exe raised exception class EIdIOHandlerPropInvalid with message "IOHandler value is not valid"
    Что это?Маша, плиз хелп. Ж-)

    ОтветитьУдалить
  30. вместо снифера юзаю плагин firebug для лисы или хрома счас раблтаю, так сказать, над регалкой аков жж с ручным вводом капчи. С инди начал знакомство недавно, так что вопрос наверное будет тупой. все параметры что которые должны идти в пост запросе записываются в виде LoginInfo.Add(username="edit1.text");
    и если значение всехпеременных является стринговыми то почему они в двойных, а не одинарных кавычках.и эти данные должны размещатся в том же порядке что и в снифере или это не важно. ещо раз извините за тупые вопросы, но есть желание научится.

    ОтветитьУдалить
  31. seoblog, в коде не .Add(username="edit1.text");
    а .Add('user=' + Edit1.Text);
    То есть пара значений в одинарных кавычках, просто формируется она из строки и свойства Text компонента Edit1. Что касается порядка, то он значения не имеет :)

    ОтветитьУдалить
  32. Здравствуйте Маша.
    Читал ваш блог уже давно, да и учился можно сказать по нему, но потом на долгое время забросил программирование. Но тут решил написать парсер яндекса, очень интересно стало :)
    Программа уже написа, всё готово, подключил куки, IOhandler - SSL,
    далее пост-запрос отправляю уверен, что правильный, но авторизация не проходит.

    Data := TStringList.Create;
    CM := TidCookieManager.Create(idhttp1);
    idHttp1.AllowCookies:=true;
    idHttp1.CookieManager := CM;
    idHttp1.HandleRedirects := true;

    IdHTTP1.Request.Host:='passport.yandex.ru';
    idHttp1.Request.UserAgent:='Opera/9.80 (Windows NT 6.1; U; MRA 5.9 (build 4848); ru) Presto/2.7.62 Version/11.00';
    idHttp1.Request.Accept:='text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
    idHttp1.Request.AcceptLanguage:='ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3';
    idhttp1.Request.Referer:='https://passport.yandex.ru/passport?mode=auth';
    idhttp1.Request.ContentType:='application/x-www-form-urlencoded';
    idhttp1.Request.Connection:='keep-alive';
    idhttp1.Request.ContentLength:=200;
    idhttp1.Request.Date:=now;
    idhttp1.Request.AcceptEncoding:='gzip,deflate';
    //IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Method:=sslvSSLv23;
    {------------------------DATA------------------}
    temp:=getidkey;
    memo1.Lines.Add(temp);
    data.Add('from=passport');
    data.Add('retpath=https://mail.yandex.ru/');
    data.Add('idkey='+temp);
    data.Add('login='+edit1.text);
    data.Add('passwd='+edit2.text);
    data.Add('timestamp='+IntToStr(DateTimeToUnix(date+time)));
    data.Add('twoweeks=yes');
    try
    StrPage:= idHttp1.Post('https://passport.yandex.ru/passport?mode=auth',data);
    s:=idHttp1.Get('https://mail.yandex.ru/');
    except
    ShowMessage('error Post');
    end;
    if Ansipos('logout',StrPage)>0 then
    ShowMessage('OK')
    else
    ShowMessage('ERROR');

    Прошу вас совета, где мне лучше рыть проблему? Или всё-таки яндекс полностью блокирует такие программы и нужно использовать яндекс АPI ?

    ОтветитьУдалить
    Ответы
    1. К сожалению, времени протестировать код сейчас нет. А так на ум ничего не приходит, почему не срабатывает. Сама уже давным-давно с Яндексом не работала :)

      Удалить

Комментарии модерируются, вопросы не по теме удаляются, троллинг тоже.

К сожалению, у меня нет столько свободного времени, чтобы отвечать на все частные вопросы, так что, может, свой вопрос лучше задать на каком-нибудь форуме?

Поделиться