При интеграции 1С со сторонними веб-сервисами и REST API разработчики часто сталкиваются с ситуацией, когда первый запрос на авторизацию проходит успешно, а последующие вызовы возвращают ошибку об отсутствии прав доступа, например NoAuth или код 401 Unauthorized. Разберем подробно, почему так происходит, как правильно настроить защищенное соединение и как поддержать активную веб-сессию между несколькими запросами.
Проанализируем архитектуру взаимодействия платформы 1С с веб-серверами. В отличие от веб-браузеров, объект платформы HTTPСоединение работает по принципу stateless (без сохранения состояния). В нем отсутствует встроенный механизм Cookie Jar (хранилище кук), который автоматически сохраняет заголовки ответа и прикрепляет их к последующим обращениям.
Многие веб-сервисы (в частности, работающие под управлением ASP.NET и IIS) построены следующим образом: метод авторизации возвращает статус подтверждения и передает в заголовке ответа Set-Cookie уникальные токены сеанса (например, ASP.NET_SessionId или пользовательские идентификаторы сессии). Если при выполнении следующего запроса эти данные не передать обратно серверу в заголовке Cookie, сервер считает клиента неавторизованным.
Вторая распространенная проблема — использование незащищенного протокола HTTP вместо HTTPS. Если сервис настроен на обязательное шифрование, сервер возвращает перенаправление (редирект), из-за чего прямой вызов завершается ошибкой или возвращает HTML-код страницы переадресации.
Выясним причину первой ошибки, с которой можно столкнуться при отправке запроса. Если при обращении к серверу возвращается ответ с кодом 301/302 или скрипт редиректа на защищенный протокол, необходимо явно инициализировать защищенное соединение.
Для работы по протоколу HTTPS конструктор объекта HTTPСоединение принимает параметр ЗащищенноеСоединение. Посмотрим на пример инициализации:
// Создаем защищенное соединение для работы по протоколу HTTPS
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
Соединение = Новый HTTPСоединение("web.fort-monitor.ru", 443,,,,, ЗащищенноеСоединение);
Параметр порта 443 является стандартным для защищенных соединений. Если его опустить, платформа при указании объекта ЗащищенноеСоединениеOpenSSL выберет порт по умолчанию.
Рассмотрим, как правильно отправить первый запрос, подтверждающий учетные данные, и забрать данные сессии из ответа сервера. Важно помнить, что если в строке запроса передаются спецсимволы, их необходимо корректно кодировать.
Посмотрим на пример отправки запроса на подключение:
// Формируем запрос на подключение
АдресПодключения = "/api/Api.svc/connect?login=demo&password=demo&lang=ru-ru&timezone=4";
ЗапросАвторизации = Новый HTTPЗапрос(АдресПодключения);
ОтветАвторизации = Соединение.Получить(ЗапросАвторизации);
Если ОтветАвторизации.КодСостояния = 200 Тогда
СтрокаОтвета = ОтветАвторизации.ПолучитьТелоКакСтроку();
// Сервер возвращает "Ok" в теле ответа
// Получаем заголовки ответа
КукиСервера = ОтветАвторизации.Заголовки.Получить("Set-Cookie");
КонецЕсли;
В свойстве ОтветАвторизации.Заголовки хранится соответствие переданных заголовков. В заголовке Set-Cookie сервер присылает строку со значениями сессионных переменных, а также директивы для браузера.
Разберем типичную ошибку при работе с куками. Некоторые разработчики пытаются передать во второй запрос заголовок Set-Cookie целиком или разбить его на отдельные пользовательские заголовки. Веб-сервер такой формат не примет.
В исходящем запросе должен присутствовать строго заголовок с именем Cookie, а его значение должно содержать пары вида Имя=Значение; Имя2=Значение2. Служебные параметры директивы (такие как path=/, HttpOnly, expires=...) предназначены исключительно для браузера и не должны передаваться обратно на сервер.
Посмотрим на пример формирования заголовка для рабочего запроса:
// Пример извлечения или подстановки нужных параметров сессии
// Строка из заголовка Set-Cookie содержит ASP.NET_SessionId и SGUID
СтрокаКуки = "ASP.NET_SessionId=tp2tth5sneljjovqtiez1inw; SGUID=session_id=05f10946-c6a1-4b2f-896d-c2623fb3dcf2&Culture=ru-ru&langfile=ext-lang-ru.js&msgfile=msg-lang-ru.js&login=";
// Создаем второй запрос для получения данных
АдресДанных = "/api/Api.svc/gettree?all=true";
ЗапросДанных = Новый HTTPЗапрос(АдресДанных);
// Устанавливаем заголовок Cookie для поддержания открытой сессии
ЗапросДанных.Заголовки.Вставить("Cookie", СтрокаКуки);
ОтветДанных = Соединение.Получить(ЗапросДанных);
ТекстОтвета = ОтветДанных.ПолучитьТелоКакСтроку();
Проанализируем еще одну частую проблему при обращении к API с фильтрацией по датам и спискам идентификаторов. В объекте HTTPЗапрос свойство АдресРесурса не выполняет автоматическое кодирование специальных символов. Если в URL присутствуют пробелы или точки с запятой (например, в датах 2014-03-07 18:00:00), сервер вернет ошибку 400 Bad Request.
Для безопасной сборки параметров используем глобальную функцию платформы КодироватьСтроку со способом кодирования СпособКодированияСтроки.КодировкаURL:
ДатаС = КодироватьСтроку("2014-03-07 18:00:00", СпособКодированияСтроки.КодировкаURL);
ДатаПо = КодироватьСтроку("2014-03-09 18:00:00", СпособКодированияСтроки.КодировкаURL);
ПараметрыОтчета = КодироватьСтроку("duration;dist;run_time", СпособКодированияСтроки.КодировкаURL);
АдресРесурса = СтрШаблон("/api/Api.svc/getobjectsreport?date_from=%1&date_to=%2¶m=%3", ДатаС, ДатаПо, ПараметрыОтчета);
ЗапросОтчета = Новый HTTPЗапрос(АдресРесурса);
ЗапросОтчета.Заголовки.Вставить("Cookie", СтрокаКуки);
ОтветОтчета = Соединение.Получить(ЗапросОтчета);
После того как авторизация подтверждена и сервер вернул полезные данные, разберем полученную строку JSON в структуры и массивы 1С для дальнейшей обработки в учетной системе. Для этого используем объекты ЧтениеJSON и функцию ПрочитатьJSON:
ЧтениеЖСОН = Новый ЧтениеJSON();
ЧтениеЖСОН.УстановитьСтроку(ОтветДанных.ПолучитьТелоКакСтроку());
// Преобразуем JSON в массив структур
Данные = ПрочитатьJSON(ЧтениеЖСОН);
ЧтениеЖСОН.Закрыть();
Для Каждого Элемент Из Данные Цикл
// Обработка полученных объектов
ИмяОбъекта = Элемент.obj_name;
Идентификатор = Элемент.oid;
КонецЦикла;
Если решение работает под управлением операционной системы Windows и ручной разбор заголовков кук вызывает сложности, рассмотрим альтернативный вариант через системный компонент WinHttp.WinHttpRequest.5.1.
Преимущество этого метода заключается в том, что системная библиотека Windows автоматически сохраняет сессионные куки между последовательными вызовами одного экземпляра объекта:
Попытка
WinHttp = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
// 1. Авторизация
URLАвторизации = "https://web.fort-monitor.ru/api/Api.svc/connect?login=demo&password=demo&lang=ru-ru&timezone=4";
WinHttp.Open("GET", URLАвторизации, Ложь);
WinHttp.Send();
// 2. Получение данных - куки передаются автоматически в рамках одного экземпляра WinHttp
URLДанных = "https://web.fort-monitor.ru/api/Api.svc/gettree?all=true";
WinHttp.Open("GET", URLДанных, Ложь);
WinHttp.Send();
Результат = WinHttp.ResponseText;
Исключение
ВызватьИсключение "Ошибка выполнения запроса через WinHttp: " + ОписаниеОшибки();
КонецПопытки;
Важное примечание: при работе в клиент-серверном варианте на серверах под управлением Linux использование COM-объектов невозможно. Поэтому в кроссплатформенных решениях всегда следует отдавать предпочтение штатному объекту HTTPСоединение с ручной передачей заголовка Cookie.
При организации регулярного обмена данными важно учитывать следующие правила:
401 или тело ответа содержит NoAuth, алгоритм должен повторно вызвать метод подключения, обновить значение куки и повторить запрос за данными.