Как исправить ошибку авторизации SOAP-запроса из 1С: 'WSS header is missing' или '401 Unauthorized'?

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

При интеграции 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 через сервисный аккаунт). Рассмотрим два способа решения этой проблемы.

Решение 1: Отправка запроса через HTTPСоединение с ручным формированием заголовка

Этот метод является скорее обходным путем, но он отлично подходит для быстрой диагностики и тестирования http сервисов и решения проблемы, когда нет времени глубоко разбираться в структуре XDTO. Суть заключается в том, чтобы перехватить успешный запрос от другого инструмента (например, SoapUI), скопировать его XML-тело вместе с правильным заголовком авторизации и отправить его из 1С с помощью объекта HTTPЗапрос.

Разберем процесс по шагам:

  1. Настройка успешного запроса в SoapUI. Сначала добейтесь, чтобы ваш запрос к веб-сервису успешно выполнялся в программе SoapUI. Эта программа корректно обрабатывает политики безопасности из WSDL и автоматически формирует необходимый заголовок <wsse:Security>.
  2. Перехват трафика с помощью Fiddler. Запустите анализатор трафика, например, Fiddler Classic. Отправьте успешный запрос из SoapUI и найдите его в Fiddler. Перейдите на вкладку "Inspectors" и выберите просмотр "Raw" или "XML" для тела запроса.
  3. Анализ и копирование XML-тела. Вы увидите полную структуру SOAP-конверта. Нас интересует всё его содержимое, особенно блок <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>) в текстовый редактор.

  4. Формирование и отправка HTTP-запроса в 1С. Теперь в коде 1С создадим и отправим запрос вручную.

    Важный момент: при таком подходе свойства Пользователь и Пароль у объекта 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С. Он особенно полезен, когда структура запроса сложна, а политики безопасности не стандартны.

Решение 2: Использование WSПрокси с ЗащищеннымСоединением и ФабрикойXDTO

Этот способ является более «правильным» и платформенным. Он не требует ручного формирования 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Параметр);
        // Обрабатываем успешный ответ
        Сообщить("Ответ получен!");
    Исключение
        Сообщить("Ошибка при вызове веб-сервиса: " + ОписаниеОшибки());
    КонецПопытки;

КонецПроцедуры

В этом решении ключевыми моментами являются:

Выбор между двумя этими методами зависит от конкретной ситуации. Первый метод быстрее в реализации, если у вас уже есть рабочий пример запроса. Второй метод более универсален и соответствует идеологии платформы 1С, но требует более глубокого понимания работы с XDTO и WS-механизмами.

← На главную