Как передать пользовательские заголовки и токен авторизации WS-Security в SOAP-запросе из 1С?

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

При интеграции систем на платформе «1С:Предприятие 8» с внешними веб-сервисами (банковскими системами, государственными порталами или корпоративными шинами) разработчики часто сталкиваются с требованием аутентификации на уровне сообщения (Message-Level Security). В отличие от стандартной HTTP-аутентификации, где логин и пароль передаются в транспортном заголовке сетевого пакета, спецификации семейства WS-* требуют размещения учетных данных непосредственно внутри структуры XML-пакета, а именно в заголовке конверта сообщения.

Типовой сценарий предполагает формирование блока безопасности в заголовке запроса, например элемента маркера безопасности с именем пользователя и паролем. Попытка решить такую задачу через стандартный механизм платформы — объект WSПрокси — вызывает затруднения, так как разработчику не удается найти методы или свойства для добавления произвольных XML-узлов в структуру исходящего конверта.

Архитектурные ограничения объекта WSПрокси в 1С

Механизм веб-сервисов в платформе «1С:Предприятие 8» разрабатывался как высокоуровневая абстракция, призванная избавить разработчика от ручной сборки XML-документов. На основании WSDL-описания платформа автоматически строит пакеты XDTO, сопоставляет типы данных встроенного языка с типами схемы XML и самостоятельно упаковывает параметры вызываемой функции в тело сообщения.

Согласно документации платформы, встроенный клиент веб-сервисов поддерживает строго ограниченный набор стандартов:

При этом платформа официально не поддерживает стандарты расширения веб-сервисов: WS-Policy, WS-Addressing и WS-Security. В объектной модели встроенного языка объект WSПрокси инкапсулирует весь процесс сетевого взаимодействия. Параметры аутентификации (пользователь и пароль), передаваемые в конструктор или свойства объекта, используются только для формирования транспортного HTTP-заголовка Authorization.

Платформа формирует узел заголовка конверта автоматически и не предоставляет разработчику программного интерфейса (API) для добавления дочерних элементов или переопределения структуры этого блока. Таким образом, передать пользовательские данные или авторизационный токен в заголовке конверта средствами объекта WSПрокси технически невозможно.

Возможные архитектурные решения задачи

Поскольку доработать поведение встроенного объекта невозможно, разработчику необходимо изменить подход к формированию запроса. На практике применяются три основных пути обхода данного ограничения:

  1. Прямая отправка запроса через низкоуровневые HTTP-объекты 1С — полный отказ от использования WSПрокси в пользу самостоятельного формирования XML-конверта и его передачи через объект HTTPСоединение. Это наиболее универсальный и независимый от стороннего ПО способ.
  2. Использование промежуточного интеграционного шлюза (Proxy / ESB) — перенаправление вызова WSПрокси на локальный вспомогательный сервис (на базе специализированной шины данных или легковесного веб-сервера), который перехватывает сообщение, внедряет необходимые заголовки безопасности и пересылает пакет конечному адресату.
  3. Разработка внешней компоненты — делегирование сетевого взаимодействия библиотеке на языке C++ или платформе .NET, где доступ к заголовкам протокола реализован нативно.

Реализация запроса через HTTPСоединение

Основной способ решения задачи в рамках типовой конфигурации без установки дополнительного программного обеспечения — самостоятельная сборка конверта и отправка его стандартным методом 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Строка(Хешер.ХешСумма);

Различия протоколов SOAP 1.1 и SOAP 1.2

При переходе от встроенного прокси к прямой отправке через HTTP-запросы важно учитывать различия версий протокола SOAP, так как несовпадение заголовков приведет к отклонению пакета веб-сервером:

Обработка ответов и разбор ошибок (SOAP Fault)

При использовании низкоуровневого объекта 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.

В таких случаях применяются следующие решения:

Критерии выбора решения

Для определения оптимального способа интеграции рекомендуется следовать правилу:

← На главную