Еще раз про авторизацию на сервере livejournal.com | jenyay.net

Еще раз про авторизацию на сервере livejournal.com

Дата публикации: 11.11.2007
Дата последней правки: 09.02.2023

Содержание

Введение
Авторизация

Заключение

Введение

Эта статья является продолжением статьи Основы работы с сервером livejournal, в которой мы рассмотрели несколько видов авторизаций, которые могут быть использованы для написания клиента наподобие Semagic или LJ.NET. В этой статье мы с вами рассмотрим еще один способ авторизации на сайте livejournal.com (ЖЖ).

Рассмотренные ранее виды авторизации позволяют сделать только те операции, которые описаны в документации. Таким образом мы не сможем, например, получить доступ к подзамочным записям друзей, посколько для доступа к чужим записям (пусть даже только для чтения) нет соответствующей функции сервера по протоколу Flat или XML-RPC (по крайней мере их нет в документации). Эта статья как раз и описывает способ прочитать такие записи.

Авторизация

Если попытаться получить доступ к подзамочным записям, используя cookies, которые были получены при авторизации одним из способов, описанных в предыдущей статье, то у нас ничего не получится. Эти Cookies не дают нам прав на просмотр сообщений только для друзей. Таким образом получается, что должен быть еще какой-то способ авторизации.

Эмуляция отправки данных с формы

Давайте рассмотрим страницу авторизации на сайте ЖЖ, расположенную по адресу http://www.livejournal.com/login.bml. Если вы уже авторизованы на этом сайте в своем браузере, то нажмите кнопку "Выход" в правом верхнем углу. Итак, на этой странице неавторизованные пользователя видят две формы для входа в систему - в правом верхнем углу и слева в центре. Как ни странно, эти две формы работают по-разному. В данной статье мы рассмотрим авторизацию с помощью эмуляции отправки данных с нижней формы.

Если проследить с помощью какого-нибудь прокси-сервера, который может показывать передаваемые данные (я предпочитаю Proxomitron), что происходит при отправке данных с помощью этой формы, то мы увидим, что на сервер передаются следующие параметры:

chalОклик (Challenge) сервера
responseСформированный с помощью пароля ответ
userИмя пользователя
passwordПароль. Значение этого параметра остается пустым
lj_form_authИдентификатор формы
action:loginЗначением этого параметра является просто надпись на кнопке для отправки данных с формы

Для авторизации требуются только первые три параметра. Параметр chal можно получить двумя способами. Можно загрузить страницу http://www.livejournal.com/login.bml и из полученного кода HTML выделить значение скрытого поля chal формы. В исходнике страницы этот параметр прописан следующим образом:

<input type='hidden' name='chal' class='lj_login_chal' value='c0:1194717600:329:300:ji9Uky2mnUSi4UpL5G9z:350a128c3e91cf7543ee3aaacd45ee46' />

Но лучше, конечно, воспользоваться функцией getchallenge сервера с помощью протокола Flat или XML-RPC. Эту функцию мы уже использовали в предыдущей статье. Значение параметра response вычисляется точно так же, как и в методе авторизации Challenge/Response, рассмотренном ранее, а именно по формуле:

MD5(chal + MD5(password))

Все эти данные надо отправлять по адресу http://www.livejournal.com/login.bml. Если все сформировано правильно, то в ответе сервера мы получим довольно много cookies. Из всех cookies для успешной загрузки подзамочных записей нам нужны будут только cookies с именами ljloggedin (таких cookies будет две штуки с одинаковыми значениями) и ljmastersession. Именно эти cookies и надо подцеплять к следующему запросу, который и будет читать записи только для друзей. Разумеется, если останутся и другие cookie, то ничего плохого не произойдет.

Надо заметить, что при получении cookies в классе HttpWebRequest из .NET Framework необходимо установить значение параметра AllowAutoRedirect в false, иначе произойдет редирект на другую страницу и мы не сохраним cookies. Зато потом при использовании полученных cookies это значение должно быть true, так как при дальнейшей загрузке страницы с этими cookies происходит серия редиректов. Если AllowAutoRedirect будет равно false, то все эти редиректы придется отслеживать вручную.

Реализация

Для демонстрации этого метода я дополнил проект LJServerTest из предыдущей статьи возможностью читать подзамочные записи. Скачать обновленные исходники можно здесь. При загрузке страницы с указанным адресом откроется новое окно, в котором будет показана загруженная страница с помощью контрола WebBrowser.

Чтение подзамочных записей осуществляет метод GetPrivatePage() класса LJServer, который принимает три параметра: адрес загружаемой страницы, логин (имя пользователя) и пароль:

public string GetPrivatePage (string url, string login, string password)
{
        CookieCollection cookies = GetBaseCookie (login, password);

        string res = GetPage (url, cookies);
        return res;
}

Здесь мы сначала получаем cookies, выдаваемые при авторизации с помощью метода GetBaseCookie, а потом загружаем страницу, используя эти cookies. Получение cookies выглядит следующим образом:

private CookieCollection GetBaseCookie (string login, string password)
{
        // Получаем оклик сервера (см. предыдущую статью)
        string lj_login_chal = GetChallenge ();

        // Рассчитаем ответ как в методе авторизации challenge / response
        string auth_response = GetAuthResponse (password, lj_login_chal);

        // Строка запроса для отправки через форму
        string textRequest = string.Format ("chal={0}&response={1}&user={2}",
                HttpUtility.UrlEncode (lj_login_chal),
                HttpUtility.UrlEncode (auth_response),
                HttpUtility.UrlEncode (login));                  


        byte[] byteArray = Encoding.UTF8.GetBytes (textRequest);

        // Получаем класс запроса
        HttpWebRequest request =
                (HttpWebRequest)WebRequest.Create ("http://www.livejournal.com/login.bml");

        // Заполняем параметры запроса
        request.Method = "POST";

        // Запрещаем автоматический редирект, чтобы сохранить cookies, полученные на первой странице после запроса
        request.AllowAutoRedirect = false;
        request.ContentType = "application/x-www-form-urlencoded";
        request.ContentLength = textRequest.Length;
        request.UserAgent = "LJServerTest";

        // Очищаем коллекцию от старых cookie и добавляем туда новые
        request.CookieContainer = new CookieContainer ();

        // Заполняем параметры Proxy (_proxy == null, если прокси не используется)
        request.Proxy = _proxy;

        // Отправляем данные запроса
        Stream requestStream = request.GetRequestStream ();
        requestStream.Write (byteArray, 0, textRequest.Length);

        // Получаем класс ответа
        HttpWebResponse response = (HttpWebResponse)request.GetResponse ();

        // Читаем ответ
        Stream responseStream = response.GetResponseStream ();
        StreamReader readStream = new StreamReader (responseStream, Encoding.UTF8);

        string currResponse = readStream.ReadToEnd ();

        readStream.Close ();
        response.Close ();

        // Оставим только самые необходимые cookies
        CookieCollection newCollection = new CookieCollection ();
        for (int i = 0; i < response.Cookies.Count; i++)
        {
                if (response.Cookies[i].Name == "ljloggedin" ||
                        response.Cookies[i].Name == "ljmastersession")
                {
                        newCollection.Add (response.Cookies[i]);
                }
        }

        return newCollection;
}

Обратите внимание, на строки кода, где формируется запрос. Не смотря на то, что при методе POST передаваемые параметры разделяются с помощью разделителя строк, здесь мы используем символ '&': "chal={0}&response={1}&user={2}". Запрос при этом будет сформирован как положено, но, если заменить амперсанды символами переводом строки, то авторизация не пройдет. В конце функции мы оставляем только cookies с именами ljloggedin и ljmastersession. Это сделано больше для демонстрации, чтобы показать, что и без других cookies все работает. В принципе, это делать не обязательно.

А для загрузки страницы с использованием cookies используется довольно простая функция:

private string GetPage (string url, CookieCollection cookies)
{
        HttpWebRequest request =
                (HttpWebRequest)WebRequest.Create (url);

        // Заполняем параметры запроса
        // Здесь мы разрешаем автоматические редиректы
        request.AllowAutoRedirect = true;

        request.Credentials = CredentialCache.DefaultCredentials;
        request.Method = "GET";
        request.CookieContainer = new CookieContainer ();

        if (cookies != null)
        {
                request.CookieContainer.Add (cookies);
        }

        // Заполняем параметры Proxy (_proxy == null, если прокси не используется)
        request.Proxy = _proxy;

        // Получаем класс ответа
        HttpWebResponse response = (HttpWebResponse)request.GetResponse ();

        // Читаем ответ
        Stream responseStream = response.GetResponseStream ();
        StreamReader readStream = new StreamReader (responseStream, Encoding.UTF8);

        string currResponse = readStream.ReadToEnd ();

        readStream.Close ();
        response.Close ();

        return currResponse;
}

Заключение

Ну вот мы и рассмотрели еще один способ авторизации на сайте livejournal.com. Интересно, сколько еще разных методов авторизации таит в себе ЖЖ? :)

Ссылки

Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.
5 stars

Рейтинг 4.6/5. Всего 9 голос(а, ов)



РУС 05.01.2009 - 10:13

СПС

СПАСИБО!!!

Jenyay 05.01.2009 - 10:25

Пожалуйста, рад, что статья пригодилась :)

Kos 15.01.2009 - 09:30

А можно каким-либо способом через coockie узнать имя пользователя из livejournal который пришел на мой сайт? Т.е. пользователь заходит на страницу моего сайта, там форма с openid, а в нее автоматом хочется подставлять его ЖЖ openid.

Jenyay 15.01.2009 - 09:47

Kos,

Посмотрел куки из ЖЖ - там нигде имя пользователя не попалось. Но можно попробовать из скрипта открыть страницу livejournal.com с куками пользователя (если такое провернуть удастся) и посмотреть в правом верхнем углу имя пользователя, если он вошел.

al 12.06.2009 - 18:50

очень хорошо

спасибо. Полезно.

Komyak 11.02.2010 - 14:46

Спасиб..

Спасибо за разбор. Реализовал это на php. Не мог понять в чём трабла получается..

Евгений 16.09.2010 - 19:02

Попробовав реализовать данный пример на Delphi, ничего не получилочь.
В исходном коде страницы www.livejournal.com/login.bml нет боле кода типа
<input type='hidden' name='chal' class='lj_login_chal' value='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' />

Jenyay 18.09.2010 - 22:08

Про Дельфи ничего не подскажу - давно с ним не имел дел :)

Евгений 19.09.2010 - 18:26

Впринципе суть не в конкретноя языке.

Проверка снифером ничего не дает (не показывает запросы), в исходном коде страницы login.bml нет переменных chal. Видимо защищаться стали(

Jenyay 19.09.2010 - 18:40

Хм, код страницы действительно поменялся. Но метод, который описан в статье все-равно работает :) Только что проверил.

Евгений 19.09.2010 - 21:06

Да, попробовал пример программы на C#, действительно работает. Но к сожалению C# я не знаю frowning smiley, будем мутить под Delphihappy smiley.

Jenyay 19.09.2010 - 21:10

Если поможет, то вот реализация той же авторизации на Python - http://jenyay.net/Programming/PyLJbot

Serg 21.10.2010 - 19:00

Я вроде авторизуюсь абсолютно также но при попытке добавить запись выводит: Ошибка при входе: не введено имя пользователя Client error: No username sent.

Стас 12.02.2011 - 16:15

Женя, подскажи, как это проделать на PHP? А то я лохонулся. Сделал парсинг комментов, а подзамочные посты не могу посмотреть. Мне нужно как то с помощью логина и пароля получить куки и вставить их в запрос. Я не особо разбираюсь в PHP. Сейчас я получаю страницу методом file_get_html. Опять же, хз как туда куки вставить. Если знаком с PHP, расскажи как это делается.

Стас 12.02.2011 - 16:17

могу сделать так. С помощью XML-RPC получить с айфона челлендж и передать на сервер. Это легко будет. Проблема с куки.

Jenyay 12.02.2011 - 17:59

Стас, с PHP не помогу, очень не люблю этот язык.


Подписаться на комментарии
Автор:
Тема:
 Ваш комментарий
 
 
Введите код 131