Как сохранить авторизацию и передавать Cookie между HTTP-запросами в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции 1С с внешними веб-сервисами и API часто возникает сценарий, когда первым запросом необходимо пройти аутентификацию (например, передав логин и пароль), а последующими обращениями получать или отправлять прикладные данные. Разработчики нередко сталкиваются с ситуацией: первый запрос авторизации завершается успешно с кодом состояния 200, однако следующий же запрос к защищенному разделу API возвращает ошибку 401 Unauthorized, будто авторизация вовсе не выполнялась.

Разберем по шагам, почему так происходит, как устроена работа с состоянием в платформе 1С и как правильно реализовать передачу сессионных cookie.

В чем причина потери авторизации при работе с HTTPСоединение

Выясним причину такого поведения. В отличие от стандартных интернет-браузеров, платформа 1С и ее объект HTTPСоединение не обладают встроенным механизмом автоматического сохранения состояния (Cookie Jar). Сам протокол HTTP по своей природе является протоколом без сохранения состояния (stateless).

Когда мы выполняем авторизацию в браузере, сервер присылает специальный заголовок Set-Cookie, содержащий идентификатор сессии. Браузер перехватывает этот заголовок, сохраняет cookie в локальное хранилище и при каждом последующем обращении к этому домену автоматически прикрепляет заголовок Cookie к исходящему запросу.

В платформе 1С объект HTTPСоединение отвечает исключительно за установление сетевого транспорта. Вся логика сохранения, парсинга и подстановки кук в каждый последующий объект HTTPЗапрос возлагается непосредственно на разработчика. Если не передать полученный идентификатор сессии во второй запрос явно, веб-сервер рассматривает входящее соединение как новое и анонимное.

Кроме того, распространенной ошибкой является попытка передать заголовки третьим параметром в метод Получить() или ОтправитьДляОбработки() объекта HTTPСоединение. Заголовки HTTP-пакета должны передаваться непосредственно в конструктор самого объекта HTTPЗапрос или устанавливаться через его свойство Заголовки.

Пошаговое решение: передача заголовков Cookie вручную

Рассмотрим, как правильно извлечь данные сессии после успешной авторизации и передать их в следующий запрос.

  1. Выполнение запроса авторизации: отправляем учетные данные на сервер и проверяем статус ответа (код 200).
  2. Извлечение заголовка сессии: считываем значение заголовка Set-Cookie из коллекции HTTPОтвет.Заголовки.
  3. Формирование заголовков следующего запроса: создаем коллекцию Соответствие, в которую помещаем ключ Cookie со значением полученной сессии.
  4. Инициализация целевого запроса: передаем сформированные заголовки в конструктор HTTPЗапрос.

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


// 1. Инициализируем соединение с сервером
HTTPСоединение = Новый HTTPСоединение("api.myshows.ru");

// 2. Отправляем запрос авторизации
HTTPЗапросАвторизации = Новый HTTPЗапрос("profile/login?login=" + Логин + "&password=" + ХешПароля);
ОтветАвторизации = HTTPСоединение.Получить(HTTPЗапросАвторизации);

Если ОтветАвторизации.КодСостояния = 200 Тогда
    
    // 3. Получаем сессионные куки из заголовков ответа
    СтрокаКуки = ОтветАвторизации.Заголовки.Получить("Set-Cookie");
    
    // 4. Подготавливаем заголовки для следующего запроса к закрытому API
    ЗаголовкиЗапроса = Новый Соответствие();
    ЗаголовкиЗапроса.Вставить("Cookie", СтрокаКуки);
    ЗаголовкиЗапроса.Вставить("Connection", "keep-alive");
    
    // 5. Передаем заголовки в конструктор HTTPЗапрос вторым параметром
    HTTPЗапросДанных = Новый HTTPЗапрос("profile/", ЗаголовкиЗапроса);
    ОтветДанных = HTTPСоединение.Получить(HTTPЗапросДанных);
    
    Сообщить("Код ответа защищенного ресурса: " + ОтветДанных.КодСостояния);
    
Иначе
    Сообщить("Ошибка авторизации. Код состояния: " + ОтветАвторизации.КодСостояния);
КонецЕсли;

Нюансы обработки заголовка Set-Cookie

Рассмотрим подробнее технические ограничения, о которых необходимо помнить при интеграции со сложными веб-сервисами:

  1. Очистка от служебных директив: Заголовок Set-Cookie от сервера часто содержит служебные параметры: path=/; domain=.site.ru; HttpOnly; Secure; Expires=.... Стандарт протокола HTTP требует, чтобы в исходящем запросе передавались только пары вида Имя=Значение; Имя2=Значение2. Некоторые веб-серверы лояльны и могут принять всю строку целиком, однако строгие шлюзы безопасности (WAF) отклонят такой запрос. В промышленных решениях рекомендуется выделять регулярным выражением или строковыми функциями только саму пару имени и значения cookie до первого символа точки с запятой.
  2. Склейка заголовков платформой 1С: Если веб-сервер устанавливает сразу несколько кук через несколько заголовков Set-Cookie, платформа 1С объединяет их в одно строковое значение через запятую. Поскольку дата истечения срока (Expires) также содержит запятую в англоязычном формате даты, разделять строку простым методом СтрРазделить() по символу запятой нельзя.
  3. Использование безопасного протокола (HTTPS): Большинство современных API запрещают передачу учетных данных по незащищенному HTTP. Для работы по HTTPS в конструктор HTTPСоединение необходимо передавать порт 443 и настроенный объект ЗащищенноеСоединениеOpenSSL.
  4. Кодирование спецсимволов в URL: При передаче параметров в строке адреса (GET-запросы) всегда используйте функцию КодироватьСтроку(Значение, СпособКодированияСтроки.КодировкаURL) для защиты от искажения данных спецсимволами.

Чтение полученного ответа в формате JSON

После того как авторизованный запрос успешно выполнен и сервер вернул код состояния 200, прикладные данные обычно передаются в теле ответа в формате JSON. Разберем по шагам, как обработать ответ встроенными средствами 1С:


Если ОтветДанных.КодСостояния = 200 Тогда
    
    // Считываем тело ответа в виде строки с указанием кодировки
    СтрокаJSON = ОтветДанных.ПолучитьТелоКакСтроку("UTF-8");
    
    // Инициализируем парсер JSON
    ЧтениеJSON = Новый ЧтениеJSON();
    ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
    
    // Десериализуем данные в структуры и массивы 1С
    ДанныеОтвета = ПрочитатьJSON(ЧтениеJSON);
    ЧтениеJSON.Закрыть();
    
    // Работаем с данными как с обычной структурой
    // Например: Сообщить(ДанныеОтвета.login);
    
КонецЕсли;

Если объем получаемых от API данных исчисляется десятками мегабайт, для экономии оперативной памяти сервера 1С вместо чтения всей строки целиком можно использовать потоковую обработку через метод ОтветДанных.ПолучитьТелоКакПоток() и метод ЧтениеJSON.ОткрытьПоток().

Альтернативное решение для Windows: объект WinHttp

Проанализируем альтернативный вариант, если разбор сложных составных cookie и редиректов средствами 1С оказывается слишком трудоемким. При работе на клиенте под управлением операционной системы Windows или на Windows-сервере можно применить системный COM-объект WinHttp.WinHttpRequest.5.1.

Преимущество этого компонента заключается в том, что операционная система берет управление сессиями на себя. Экземпляр объекта WinHttp автоматически сохраняет все полученные cookies и подставляет их в последующие вызовы в рамках одного скрипта без необходимости парсить строки вручную:


Попытка
    HTTPЗапросCOM = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
    
    // 1. Авторизация
    HTTPЗапросCOM.Open("GET", "https://api.myshows.ru/profile/login?login=" + Логин + "&password=" + ХешПароля, Ложь);
    HTTPЗапросCOM.Send();
    
    Если HTTPЗапросCOM.Status = 200 Тогда
        // 2. Запрос данных - куки подставятся операционной системой автоматически
        HTTPЗапросCOM.Open("GET", "https://api.myshows.ru/profile/", Ложь);
        HTTPЗапросCOM.Send();
        
        ТекстОтвета = HTTPЗапросCOM.ResponseText;
        Сообщить("Статус запроса: " + HTTPЗапросCOM.Status);
    КонецЕсли;
Исключение
    Сообщить("Ошибка вызова WinHttp: " + ОписаниеОшибки());
КонецПопытки;

Однако помните, что вызов COM-объектов платформозависим и не будет работать на серверах под управлением Linux, поэтому для кроссплатформенных конфигураций приоритетным остается использование штатных объектов HTTPСоединение и HTTPЗапрос с ручной передачей заголовка Cookie.

← На главную