Авторизация из приложения C# на портале BlaBlaCar.ru

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Зачем ?

Из кода Вашего приложения Вы можете авторизоваться на любом web-портале. Это может быть нужно когда Вы хотите получить доступ к ресурсам Вашего аккаунта.

Что можно получить на данном портале ?

Вы можете получить список Ваших поездок, а можете проверить какие поездки запланированы на всем портале от пункта "A" в пункт "B" на конкретную дату. Вы можете больше.

Что понадобится ?

  • Локальный прокси-сервер, рекомендую Fiddler - просто и удобно

  • Visual Studio

Соберем данные

Работа web-клиента и web-сервера происходит по протоколу HTTP. Клиент отправляет HTTP запросы в виде заголовков, сервер отправляет ответ в виде заголовков. Это значит что для нашей задачи нужно отправить на сервер несколько запросов чтобы авторизоваться и получить необходимые данные. Чтобы узнать список запросов мы воспользуемся программой Fiddler. После запуска Fiddler откроем любой браузер, перейдем на портал blablacar.ru, авторизуемся и получим список необходимых HTTP-запросов (лишние запросы удалены).

Перейдем к коду. Авторизация.

Для выполнения HTTP-запросов используется класс HttpWebRequest. Воспользуемся несложными обертками для этого класса: классы GetRequest и PostRequest.

GetRequest
    public class GetRequest
    {
        private HttpWebRequest _request;
        public void Run(ref CookieContainer cookies)
        {
            _request = (HttpWebRequest)WebRequest.Create(Address);

            _request.Headers.Add("DNT", "1");
            _request.Method = "Get";
            _request.Accept = Accept;
            _request.Host = Host;

            if (TurnOffProxy) _request.Proxy = null;
            else _request.Proxy = Proxy;

            if (UseUnsafeHeaderParsing)
            {
                var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                var settings = (SettingsSection)config.GetSection("system.net/settings");
                settings.HttpWebRequest.UseUnsafeHeaderParsing = true;
                config.Save(ConfigurationSaveMode.Modified);
                ConfigurationManager.RefreshSection("system.net/settings");
            }

            _request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;

            if (!ContentType.IsEmpty()) _request.ContentType = ContentType;

            if (TimeOut > 0)
            {
                _request.Timeout = TimeOut;
                _request.ReadWriteTimeout = TimeOut;
            }
            else
            {
                _request.Timeout = 35000;
                _request.ReadWriteTimeout = 35000;
            }

            if (NoCachePolicy == false)
            {
                var noCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
                _request.CachePolicy = noCachePolicy;
            }

            foreach (KeyValuePair<string, string> keyValuePair in Headers)
            {
                _request.Headers.Add(keyValuePair.Key, keyValuePair.Value);
            }

            if (UserAgent == null) _request.UserAgent = Data.Ie11;
            else _request.UserAgent = UserAgent;


            if (AllowAutoRedirect != null)
                _request.AllowAutoRedirect = (bool)AllowAutoRedirect;

            if (KeepAlive != null)
                _request.KeepAlive = (bool)KeepAlive;

            if (Expect100Continue != null)
                _request.ServicePoint.Expect100Continue = (bool)Expect100Continue;


            if (!Referer.IsEmpty())
                _request.Referer = Referer;

            _request.CookieContainer = cookies;

            try
            {
                HttpWebResponse response = (HttpWebResponse)_request.GetResponse();

                if ((response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect) && response.ContentType.StartsWith("image", StringComparison.OrdinalIgnoreCase))
                {
                    // if the remote file was found, download oit
                    using (Stream inputStream = response.GetResponseStream())
                    {
                        byte[] buffer = new byte[64000];
                        int bytesRead;

                        bytesRead = inputStream.Read(buffer, 0, buffer.Length);
                        Response = Convert.ToBase64String(buffer, 0, bytesRead);
                    }
                }
                else
                {
                    var stream = response.GetResponseStream();

                    if (stream != null) Response = new StreamReader(stream).ReadToEnd();
                    ResponseHeaders = response.Headers;
                    RequestHeaders = _request.Headers;
                }


                response.Close();
            }
            catch (WebException ex)
            {
                using (var stream = ex.Response.GetResponseStream())
                using (var reader = new StreamReader(stream))
                {
                    Response = reader.ReadToEnd();
                }
            }
            catch (Exception ex)
            {
            }

        }

        Dictionary<string, string> Headers = new Dictionary<string, string>();
        public void AddHeader(string headerName, string headerValue)
        {
            Headers[headerName] = headerValue;
        }
        public bool NoCachePolicy { get; set; }
        public bool AcceptGZipEncoding { get; set; }
        public bool UseUnsafeHeaderParsing { get; set; }
        public string Address { get; set; }
        public string Accept { get; set; }
        public string Referer { get; set; }
        public string Host { get; set; }
        public bool? KeepAlive { get; set; }
        public string ContentType { get; set; }
        public bool? Expect100Continue { get; set; }
        public string Response { get; private set; }
        public bool? AllowAutoRedirect { get; set; }
        public WebHeaderCollection ResponseHeaders { get; private set; }
        public WebHeaderCollection RequestHeaders { get; private set; }
        public string UserAgent { get; set; }
        public WebProxy Proxy { get; set; }
        public bool TurnOffProxy { get; set; }
        public int TimeOut { get; set; }

    }

PostRequest
    public class PostRequest
    {
        private HttpWebRequest _request;
        public void Run(ref CookieContainer cookies)
        {
            _request = (HttpWebRequest)WebRequest.Create(Address);
            _request.Method = "POST";
            _request.Host = Host;
            _request.Headers.Add("DNT", "1");
            _request.Proxy = Proxy;

            _request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;

            if (TimeOut > 0)
            {
                _request.Timeout = TimeOut;
                _request.ReadWriteTimeout = TimeOut;
            }
            else
            {
                _request.Timeout = 90000;
                _request.ReadWriteTimeout = 90000;
            }

            var noCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
            _request.CachePolicy = noCachePolicy;

            if (Expect100Continue == true) _request.ServicePoint.Expect100Continue = true;
            else _request.ServicePoint.Expect100Continue = false;

            _request.ContentType = ContentType;
            _request.Accept = Accept;
            _request.Referer = Referer;

            _request.KeepAlive = KeepAlive;

            foreach (KeyValuePair<string, string> keyValuePair in Headers)
            {
                _request.Headers.Add(keyValuePair.Key, keyValuePair.Value);
            }

            _request.CookieContainer = cookies;

            if (UserAgent == null) _request.UserAgent = Dom.Catalog.Data.Ie11;
            else _request.UserAgent = UserAgent;

            if (AllowAutoRedirect != null)
                _request.AllowAutoRedirect = (bool)AllowAutoRedirect;


            byte[] sentData;
            if (ByteData != null) sentData = ByteData;
            else sentData = Encoding.UTF8.GetBytes(Data);


            _request.ContentLength = sentData.Length;
            Stream sendStream = _request.GetRequestStream();
            sendStream.Write(sentData, 0, sentData.Length);
            sendStream.Close();
            WebResponse response = _request.GetResponse();

            ResponseHeaders = response.Headers;
            RequestHeaders = _request.Headers;

            Stream stream = response.GetResponseStream();
            if (stream != null)
            {
                if (ResponseEncoding.IsEmpty()) Response = new StreamReader(stream).ReadToEnd();
                else Response = new StreamReader(stream, Encoding.GetEncoding(ResponseEncoding)).ReadToEnd();
            }
            response.Close();
        }

        Dictionary<string, string> Headers = new Dictionary<string, string>();
        public void AddHeader(string headerName, string headerValue)
        {
            Headers[headerName] = headerValue;
        }
        public bool NoCachePolicy { get; set; }
        public string Response { get; set; }
        public string ResponseEncoding { get; set; }
        public string Data { get; set; }
        public byte[] ByteData { get; set; }
        public string Address { get; set; }
        public string Accept { get; set; }
        public string Host { get; set; }
        public string ContentType { get; set; }
        public string Referer { get; set; }
        public bool KeepAlive { get; set; }
        public bool? Expect100Continue { get; set; }
        public string UserAgent { get; set; }
        public bool? AllowAutoRedirect { get; set; }
        public WebHeaderCollection ResponseHeaders { get; private set; }
        public WebHeaderCollection RequestHeaders { get; private set; }
        public WebProxy Proxy { get; set; }
        public int TimeOut { get; set; }
    }

В переменные user и password необходимо внести значения Вашего аккаунта.

В cookies у нас будут храниться все cookie выполненных запросов.

proxy - это адрес Вашего прокси. Например, "127.0.0.1:8888" - стандартный адрес запущенного Fiddler.

SecurityProtocolType.Tls12 - указание используемого протокола безопасности.

var user = "user";              // !!! rewrite value from your account !!!
var password = "password";      // !!! rewrite value from your account !!!

// We keep cookies here
var cookies = new CookieContainer();

// Any proxy, for example Fiddler
var proxy = new WebProxy("127.0.0.1:8888");

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

Для выполнения авторизации на портале необходимо получить cookie и некоторые переменные, которые хранятся в результате запроса "https://www.blablacar.ru/".

var getRequest = new GetRequest()
{
     Address = "https://www.blablacar.ru/",
     Accept = "text/html, application/xhtml+xml, image/jxr, */*",
     Host = "www.blablacar.ru",
     KeepAlive = true,
     UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko",
     Proxy = proxy
};
getRequest.Run(ref cookies);

Используя нехитрые методы работы с текстом, находим значение переменной "visitorId":

var visitorStart = getRequest.Response.IndexOf("visitorId") + 12;
var visitorEnd = getRequest.Response.IndexOf("\"", visitorStart);
var visitorId = getRequest.Response.Substring(visitorStart, visitorEnd - visitorStart);

В переменную xCorrelationId необходимо передать случайный GUID.

Теперь все значения готовы чтобы выполнить авторизацию на портале.

// Random value
var xCorrelationId = Guid.NewGuid();

// Auth request
var data = $"{{\"login\":\"{user}\",\"password\":\"{password}\",\"rememberMe\":true,\"grant_type\":\"password\"}}";
var postRequest = new PostRequest()
{
    Data = data,
    Address = $"https://auth.blablacar.ru/secure-token",
    Accept = "application/json",
    Host = "auth.blablacar.ru",
    ContentType = "application/json",
    Referer = "https://www.blablacar.ru/login/email",
    KeepAlive = true,
    Proxy = proxy
};
postRequest.AddHeader("x-client", "SPA|1.0.0");
postRequest.AddHeader("x-correlation-id", xCorrelationId.ToString());
postRequest.AddHeader("x-currency", "RUB");
postRequest.AddHeader("x-forwarded-proto", "https");
postRequest.AddHeader("x-locale", "ru_RU");
postRequest.AddHeader("x-visitor-id", visitorId);
postRequest.AddHeader("Origin", "https://www.blablacar.ru");
postRequest.Run(ref cookies);

В ответе на POST-запрос авторизации мы должны получить значение еще одной переменной, назовем ее "bearerCookieValue". Это значение хранится в заголовках "Set-Cookie" ответа.

var bearerCookieValue = string.Empty;
var headers = postRequest.ResponseHeaders;
for (int i = 0; i < headers.Count; ++i)
{
    string header = headers.GetKey(i);

    if (header == "Set-Cookie")
    {
         var headerValue = headers.GetValues(i)[0];
         headerValue = WebUtility.UrlDecode(headerValue);

         var accessTokenStart = postRequest.Response.IndexOf("access_token") + 15;
         var accessTokenEnd = postRequest.Response.IndexOf("\"", accessTokenStart);
         bearerCookieValue = postRequest.Response.Substring(accessTokenStart, accessTokenEnd - accessTokenStart);
    }
}

Список выполненных поездок

Теперь все готово чтобы выполнять запросы за необходимой информацией. Весь код сформирован на основании просмотренных заголовков запроса/ответа в прокси-сервере Fiddler.

getRequest = new GetRequest()
{
    Address = "https://edge.blablacar.ru/bookings-and-tripoffers?active=false",
    Accept = "application/json",
    Host = "edge.blablacar.ru",
    KeepAlive = true,
    ContentType = "application/json",
    Referer = "https://www.blablacar.ru/rides/history",
    Proxy = proxy
};
getRequest.AddHeader("x-blablacar-accept-endpoint-version", "2");
getRequest.AddHeader("x-client", "SPA|1.0.0");
getRequest.AddHeader("x-correlation-id", xCorrelationId.ToString());
getRequest.AddHeader("x-currency", "RUB");
getRequest.AddHeader("x-forwarded-proto", "https");
getRequest.AddHeader("x-locale", "ru_RU");
getRequest.AddHeader("x-visitor-id", visitorId);
getRequest.AddHeader("authorization", $"Bearer {bearerCookieValue}");
getRequest.AddHeader("Origin", "https://www.blablacar.ru");
getRequest.Run(ref cookies);

Ответ получаем в виде JSON-формата:

Находим список всех поездок на портале

В данном примере найдем список всех поездок из Москвы в Санкт-Петербург на 15 декабря 2020 года.

var date = "2020-12-15";
var searchUid = Guid.NewGuid();

getRequest = new GetRequest()
{
    Address = $"https://edge.blablacar.ru/trip/search?from_coordinates=55.755826%2C37.617299&from_country=RU&to_coordinates=59.931058%2C30.360909&to_country=RU&departure_date={date}&min_departure_time=00%3A00%3A00&requested_seats=1&passenger_gender=UNKNOWN&search_uuid={searchUid}",
    Accept = "application/json",
    Host = "edge.blablacar.ru",
    KeepAlive = true,
    ContentType = "application/json",
    Referer = "https://www.blablacar.ru/",
    Proxy = proxy
};
getRequest.AddHeader("x-blablacar-accept-endpoint-version", "2");
getRequest.AddHeader("x-client", "SPA|1.0.0");
getRequest.AddHeader("x-correlation-id", xCorrelationId.ToString());
getRequest.AddHeader("x-currency", "RUB");
getRequest.AddHeader("x-forwarded-proto", "https");
getRequest.AddHeader("x-locale", "ru_RU");
getRequest.AddHeader("x-visitor-id", visitorId);
getRequest.AddHeader("authorization", $"Bearer {bearerCookieValue}");
getRequest.AddHeader("Origin", "https://www.blablacar.ru");
getRequest.Run(ref cookies);

В ответе на запрос получаем JSON-строку с поездками на портале:

Готово

Источник: https://habr.com/ru/post/532788/


Интересные статьи

Интересные статьи

Привет! На связи IceRock Development, команда из Новосибирска. Хотим поделиться полезным опытом. Вот уже 2 года мы занимаемся разработкой на Kotlin Multiplatform. В 2018 году начали реа...
Здравствуйте! Меня зовут Ренат Саматов, я возглавляю фронтенд-направление в линейке продуктов Витрина. Большая часть наших проектов рендерится на сервере. Сегодня мы побеседуем об...
Здравствуйте, Хабр! Мы изучаем возможность издания книги на тему «PWA» (Progressive Web Applications). На Хабре уже немало публикаций на эту тему, особенно интересен материал из блога компании RU...
В преддверии старта курса «React.js разработчик» подготовили перевод полезного материала. Как масштабируется front-end вашего приложения? Как сделать так, чтобы ваш код можно было поддержи...
Не так давно мне было необходимо написать несколько ansible playbooks для подготовки сервера к деплою rails приложения. И, на удивление, я не нашел простого пошагового мануала. Копировать чужой п...