При интеграции 1С с внешними SOAP-сервисами разработчики часто сталкиваются с ошибками авторизации (в некоторых случаях может потребоваться даже подпись RSA с хешированием). Стандартный механизм через свойства Пользователь и Пароль объекта WSПрокси не всегда срабатывает, и система возвращает ошибки вида «WSS header is missing» или «Error 401--Unauthorized». Давайте разберемся, почему это происходит и как настроить корректное подключение.
Основная причина таких ошибок кроется в различии стандартов аутентификации. Свойства .Пользователь и .Пароль в 1С отправляют данные через механизм HTTP Basic Authentication. Это означает, что логин и пароль кодируются и передаются в заголовках HTTP-протокола. Однако многие современные, особенно корпоративные (SAP, Oracle) и государственные веб-сервисы (например, запросы к ВетИС), требуют более сложный и безопасный стандарт — WS-Security (WSS). При этом подходе учетные данные встраиваются непосредственно в тело XML-сообщения в специальный SOAP-заголовок <wsse:Security>.
Платформа 1С при создании WSПрокси из WSDL-файла часто игнорирует инструкции по безопасности (секции <wsp:Policy>), поэтому не формирует нужный заголовок автоматически (подобные сложности возникают и при интеграции с Google через сервисный аккаунт). Рассмотрим два способа решения этой проблемы.
Этот метод является скорее обходным путем, но он отлично подходит для быстрой диагностики и тестирования http сервисов и решения проблемы, когда нет времени глубоко разбираться в структуре XDTO. Суть заключается в том, чтобы перехватить успешный запрос от другого инструмента (например, SoapUI), скопировать его XML-тело вместе с правильным заголовком авторизации и отправить его из 1С с помощью объекта HTTPЗапрос.
Разберем процесс по шагам:
<wsse:Security>.
<soapenv:Header>. Он будет выглядеть примерно так:
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="...">
<wsse:UsernameToken wsu:Id="...">
<wsse:Username>ВашЛогин</wsse:Username>
<wsse:Password Type="...">ВашПароль</wsse:Password>
<wsse:Nonce EncodingType="...">...</wsse:Nonce>
<wsu:Created>...</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
Скопируйте всё тело запроса (от <soapenv:Envelope ...> до </soapenv:Envelope>) в текстовый редактор.
Важный момент: при таком подходе свойства Пользователь и Пароль у объекта HTTPСоединение указывать не нужно, так как вся информация для авторизации уже содержится в теле запроса.
// Текст нашего SOAP-запроса, скопированный из Fiddler
// Параметры можно подставлять динамически, заменяя строки
ТекстЗапросаXML = `
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="...">
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-1">
<wsse:Username>МойЛогин</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">МойПароль</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<urn:ReadActivities>
<!-- Параметры метода -->
</urn:ReadActivities>
</soapenv:Body>
</soapenv:Envelope>`;
// Создаем соединение. Логин и пароль здесь не указываем!
// Если сервис работает по HTTPS, порт будет 443, и нужно использовать ЗащищенноеСоединение
Сервер = "адрес.сервиса.ру";
SSL = Новый ЗащищенноеСоединениеOpenSSL(); // Может потребоваться передать сертификаты
Соединение = Новый HTTPСоединение(Сервер, 443, , , , , SSL);
// Создаем HTTP-запрос
Запрос = Новый HTTPЗапрос;
Запрос.АдресРесурса = "/путь/к/сервису"; // Например, /ws/service
// Устанавливаем заголовки. Content-Type и SOAPAction обязательны
Запрос.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
Запрос.Заголовки.Вставить("SOAPAction", "urn:ReadActivities"); // Значение SOAPAction берем из Fiddler
// Устанавливаем тело запроса
Запрос.УстановитьТелоИзСтроки(ТекстЗапросаXML, КодировкаТекста.UTF8);
// Отправляем запрос
Попытка
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния = 200 Тогда
Сообщить("Запрос успешно выполнен!");
// Дальше можно разбирать Ответ.ПолучитьТелоКакСтроку()
Иначе
Сообщить("Ошибка: " + Ответ.КодСостояния + " " + Ответ.ПолучитьТелоКакСтроку());
КонецЕсли;
Исключение
Сообщить("Не удалось выполнить запрос: " + ОписаниеОшибки());
КонецПопытки;
Этот метод надежен и позволяет обойти ограничения платформы 1С. Он особенно полезен, когда структура запроса сложна, а политики безопасности не стандартны.
Этот способ является более «правильным» и платформенным. Он не требует ручного формирования XML, но может потребовать более тщательной настройки объектов 1С. Этот подход хорошо работает для сервисов, требующих авторизацию по WS-Security поверх защищенного канала HTTPS.
Проанализируем пример кода для работы с таким сервисом.
Процедура ВыполнитьЗапросКСервисуGS1()
// 1. Создаем защищенное соединение. Это критически важно для WS-Security,
// так как пароль в теле XML передается в открытом виде (PasswordText)
// и канал должен быть зашифрован.
SSL = Новый ЗащищенноеСоединениеOpenSSL;
// 2. Создаем определения веб-сервиса. В данном примере логин и пароль
// передаются уже на этом этапе, что может быть требованием конкретного сервиса.
Определения = Новый WSОпределения("https://service.url/service?wsdl", "4640072349995", "!UNi$C@n21#", , 60, , SSL);
// 3. Создаем WS-прокси, передавая ему объект определений и SSL-соединение.
Прокси = Новый WSПрокси(Определения,
"urn:org.gs1ru.gs46.intf",
"GS1RU_Operations",
"GS1RU_OperationsControllerPort",
,
60,
SSL);
// 4. Устанавливаем пользователя и пароль для прокси.
// Это может дублировать предыдущую настройку, но часто необходимо для формирования
// нужного SOAP-заголовка платформой.
Прокси.Пользователь = "4640072349995";
Прокси.Пароль = "!UNi$C@n21#";
// 5. Работа с параметрами через ФабрикуXDTO.
// Для сложных типов данных это единственно верный путь.
// Получаем описание типа для параметра метода "GetItemByGTIN"
ТипWSПараметра = Прокси.ФабрикаXDTO.Пакеты.Получить("urn:org.gs1ru.gs46.intf").Получить("GetItemByGTIN");
// Создаем объект-параметр на основе этого типа
WSПараметр = Прокси.ФабрикаXDTO.Создать(ТипWSПараметра);
// Получаем базовые типы XDTO для создания значений
ТипBoolean = Прокси.ФабрикаXDTO.Тип("http://www.w3.org/2001/XMLSchema", "boolean");
ТипString = Прокси.ФабрикаXDTO.Тип("http://www.w3.org/2001/XMLSchema", "string");
// Создаем значения для каждого поля параметра
GTIN = Прокси.ФабрикаXDTO.Создать(ТипString, "4630080790056");
Lang = Прокси.ФабрикаXDTO.Создать(ТипString, "ru");
ShowMeta = Прокси.ФабрикаXDTO.Создать(ТипBoolean, Истина); // или XMLСтрока(1)
// 6. Заполняем поля сложного параметра
WSПараметр.lang = Lang;
WSПараметр.showMeta = ShowMeta;
// ... заполняем остальные поля ...
WSПараметр.GTIN.Добавить(GTIN); // Пример для поля-коллекции
// 7. Вызываем метод веб-сервиса
Попытка
ОтветСервиса = Прокси.GetItemByGTIN(WSПараметр);
// Обрабатываем успешный ответ
Сообщить("Ответ получен!");
Исключение
Сообщить("Ошибка при вызове веб-сервиса: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
В этом решении ключевыми моментами являются:
ЗащищенноеСоединениеOpenSSL: Обязательно для HTTPS и является требованием стандарта WS-Security при передаче пароля текстом.WSОпределения и WSПрокси: Это указывает платформе на необходимость использования защищенного канала и позволяет ей корректно сформировать запрос.ФабрикаXDTO: Для методов со сложными параметрами необходимо явно создавать объекты данных через фабрику, определяя их типы и заполняя свойства. Прямая передача структур или соответствий не сработает.Выбор между двумя этими методами зависит от конкретной ситуации. Первый метод быстрее в реализации, если у вас уже есть рабочий пример запроса. Второй метод более универсален и соответствует идеологии платформы 1С, но требует более глубокого понимания работы с XDTO и WS-механизмами.