При интеграции систем на платформе «1С:Предприятие 8» с внешними веб-сервисами (банковскими системами, государственными порталами или корпоративными шинами) разработчики часто сталкиваются с требованием аутентификации на уровне сообщения (Message-Level Security). В отличие от стандартной HTTP-аутентификации, где логин и пароль передаются в транспортном заголовке сетевого пакета, спецификации семейства WS-* требуют размещения учетных данных непосредственно внутри структуры XML-пакета, а именно в заголовке конверта сообщения.
Типовой сценарий предполагает формирование блока безопасности в заголовке запроса, например элемента маркера безопасности с именем пользователя и паролем. Попытка решить такую задачу через стандартный механизм платформы — объект WSПрокси — вызывает затруднения, так как разработчику не удается найти методы или свойства для добавления произвольных XML-узлов в структуру исходящего конверта.
Механизм веб-сервисов в платформе «1С:Предприятие 8» разрабатывался как высокоуровневая абстракция, призванная избавить разработчика от ручной сборки XML-документов. На основании WSDL-описания платформа автоматически строит пакеты XDTO, сопоставляет типы данных встроенного языка с типами схемы XML и самостоятельно упаковывает параметры вызываемой функции в тело сообщения.
Согласно документации платформы, встроенный клиент веб-сервисов поддерживает строго ограниченный набор стандартов:
При этом платформа официально не поддерживает стандарты расширения веб-сервисов: WS-Policy, WS-Addressing и WS-Security. В объектной модели встроенного языка объект WSПрокси инкапсулирует весь процесс сетевого взаимодействия. Параметры аутентификации (пользователь и пароль), передаваемые в конструктор или свойства объекта, используются только для формирования транспортного HTTP-заголовка Authorization.
Платформа формирует узел заголовка конверта автоматически и не предоставляет разработчику программного интерфейса (API) для добавления дочерних элементов или переопределения структуры этого блока. Таким образом, передать пользовательские данные или авторизационный токен в заголовке конверта средствами объекта WSПрокси технически невозможно.
Поскольку доработать поведение встроенного объекта невозможно, разработчику необходимо изменить подход к формированию запроса. На практике применяются три основных пути обхода данного ограничения:
Основной способ решения задачи в рамках типовой конфигурации без установки дополнительного программного обеспечения — самостоятельная сборка конверта и отправка его стандартным методом POST протокола HTTP. Этот подход обеспечивает полный контроль над каждым байтом исходящего XML-документа.
Структура формируемого сообщения должна содержать корневой элемент конверта, заголовок с данными безопасности и тело сообщения с бизнес-параметрами вызова:
<soapenv:Envelope xmlns:soapenv="https://example.com/soap/envelope/"
xmlns:wsse="https://example.com/wss/security/"
xmlns:srv="https://example.com/service/">
<soapenv:Header>
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>integration_user</wsse:Username>
<wsse:Password Type="https://example.com/wss/PasswordText">SecretPassword</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<srv:GetDataRequest>
<srv:DocumentNumber>DOC-100200</srv:DocumentNumber>
</srv:GetDataRequest>
</soapenv:Body>
</soapenv:Envelope>
Для программного формирования такого пакета во встроенном языке 1С рекомендуется использовать объект ЗаписьXML. Это гарантирует корректную работу с кодировками, пространствами имен и экранированием спецсимволов. Ниже приведен пример процедуры отправки запроса:
// 1. Формирование тела XML-конверта
ЗаписьXML = Новый ЗаписьXML;
ПараметрыЗаписи = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Ложь);
ЗаписьXML.УстановитьСтроку(ПараметрыЗаписи);
ЗаписьXML.ЗаписатьНачалоЭлемента("soapenv:Envelope");
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен("soapenv", "https://example.com/soap/envelope/");
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен("wsse", "https://example.com/wss/security/");
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен("srv", "https://example.com/service/");
// Блок заголовка с авторизацией
ЗаписьXML.ЗаписатьНачалоЭлемента("soapenv:Header");
ЗаписьXML.ЗаписатьНачалоЭлемента("wsse:Security");
ЗаписьXML.ЗаписатьНачалоЭлемента("wsse:UsernameToken");
ЗаписьXML.ЗаписатьНачалоЭлемента("wsse:Username");
ЗаписьXML.ЗаписатьТекст("integration_user");
ЗаписьXML.ЗаписатьКонецЭлемента(); // wsse:Username
ЗаписьXML.ЗаписатьНачалоЭлемента("wsse:Password");
ЗаписьXML.ЗаписатьАтрибут("Type", "https://example.com/wss/PasswordText");
ЗаписьXML.ЗаписатьТекст("SecretPassword");
ЗаписьXML.ЗаписатьКонецЭлемента(); // wsse:Password
ЗаписьXML.ЗаписатьКонецЭлемента(); // wsse:UsernameToken
ЗаписьXML.ЗаписатьКонецЭлемента(); // wsse:Security
ЗаписьXML.ЗаписатьКонецЭлемента(); // soapenv:Header
// Блок тела сообщения
ЗаписьXML.ЗаписатьНачалоЭлемента("soapenv:Body");
ЗаписьXML.ЗаписатьНачалоЭлемента("srv:GetDataRequest");
ЗаписьXML.ЗаписатьНачалоЭлемента("srv:DocumentNumber");
ЗаписьXML.ЗаписатьТекст("DOC-100200");
ЗаписьXML.ЗаписатьКонецЭлемента(); // srv:DocumentNumber
ЗаписьXML.ЗаписатьКонецЭлемента(); // srv:GetDataRequest
ЗаписьXML.ЗаписатьКонецЭлемента(); // soapenv:Body
ЗаписьXML.ЗаписатьКонецЭлемента(); // soapenv:Envelope
СтрокаXML = ЗаписьXML.Закрыть();
// 2. Настройка сетевого соединения и заголовков
АдресСервера = "api.example.com";
ПутьКСервису = "/soap/v1/service.endpoint";
SSLСоединение = Новый ЗащищенноеСоединениеOpenSSL;
Соединение = Новый HTTPСоединение(АдресСервера, 443, , , , 30, SSLСоединение);
Запрос = Новый HTTPЗапрос(ПутьКСервису);
Запрос.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
Запрос.Заголовки.Вставить("SOAPAction", "https://example.com/service/GetData");
Запрос.УстановитьТелоИзСтроки(СтрокаXML, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
// 3. Отправка и получение ответа
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
СтрокаОтвета = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
Стандарт безопасности предусматривает два варианта передачи пароля в элементе маркера учетных данных:
Если внешний сервис ожидает вариант с вычислением дайджеста, во встроенном языке 1С расчет выполняется с помощью объектов ХешированиеДанных и функций преобразования двоичных данных:
// Генерация временной метки создания запроса в стандарте UTC
ВременнаяМетка = Формат(ТекущаяУниверсальнаяДата(), "ДФ=yyyy-MM-ddTHH:mm:ss'Z'");
// Формирование криптографически стойкого или псевдослучайного Nonce
ГСЧ = Новый ГенераторСлучайныхЧисел;
БуферNonce = Новый ДвоичныеДанные(ПолучитьИмяВременногоФайла()); // либо сборка массива байт
// После объединения двоичных представлений Nonce + Created + Password:
Хешер = Новый ХешированиеДанных(ХешФункция.SHA1);
Хешер.Добавить(ОбъединенныеДвоичныеДанные);
ПарольныйДайджест = Base64Строка(Хешер.ХешСумма);
При переходе от встроенного прокси к прямой отправке через HTTP-запросы важно учитывать различия версий протокола SOAP, так как несовпадение заголовков приведет к отклонению пакета веб-сервером:
Content-Type должен иметь значение text/xml; charset=utf-8. Название вызываемого метода передается в отдельном обязательном HTTP-заголовке SOAPAction (значение часто заключается в двойные кавычки).Content-Type принимает значение application/soap+xml; charset=utf-8; action="...". Отдельный транспортный заголовок SOAPAction в этой версии не используется и является ошибкой.При использовании низкоуровневого объекта HTTPСоединение разработчик должен самостоятельно обрабатывать нештатные ситуации. Если веб-сервис обнаруживает ошибку (неверный токен, отсутствие доступа, нарушение схемы XML), сервер возвращает код состояния HTTP 500 (Internal Server Error) или 400 (Bad Request).
В отличие от обычных сбоев веб-сервера, в протоколе SOAP при коде ответа 500 возвращается валидный XML-документ, содержащий элемент Fault. Попытка вызвать исключение только по факту кода 500 приведет к потере важной диагностической информации. Ответ необходимо обработать:
Если Ответ.КодСостояния = 200 Тогда
// Успешный вызов: чтение полезных бизнес-данных из soapenv:Body
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СтрокаОтвета);
// Десериализация данных через ФабрикуXDTO или прямое чтение узлов
Иначе
// Анализ ошибки сервиса
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СтрокаОтвета);
Пока ЧтениеXML.Прочитать() Цикл
Если ЧтениеXML.Имя = "faultstring" Тогда
ТекстОшибки = ЧтениеXML.ПрочитатьСтроку();
ВызватьИсключение "Ошибка веб-сервиса: " + ТекстОшибки;
КонецЕсли;
КонецЦикла;
КонецЕсли;
В сценариях, когда требования к безопасности выходят за рамки простой передачи логина и пароля, ручная сборка сообщений в 1С становится неэффективной. Это характерно для требований создания цифровой подписи каждого сообщения по стандарту XML-DSig или шифрования тела сообщения по стандарту XML-Encryption с использованием алгоритмов ГОСТ или RSA.
В таких случаях применяются следующие решения:
Для определения оптимального способа интеграции рекомендуется следовать правилу: