Как получить Bearer токен для 1С:Элемент из 1С:Предприятия и избежать ошибки "400 Bad Request"?

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

При работе с двумя разными платформами 1С, такими как 1С:Элемент и обычное приложение 1С:Предприятие, часто возникает необходимость их интеграции. Одной из распространенных задач является получение авторизационного токена (Bearer токен) для доступа к HTTP-сервисам 1С:Элемент из кода, написанного на встроенном языке 1С:Предприятия. Мы подробно рассмотрим проблему, с которой сталкиваются многие разработчики – ошибку "400 Bad Request" – и представим пошаговое решение, основанное на правильном понимании механизмов HTTP-запросов в 1С:Предприятие.

Основная сложность возникает, когда код, успешно работающий в среде 1С:Элемент или при тестировании через инструменты, подобные Postman, выдает ошибку "400 Bad Request" при его переносе в конфигурацию 1С:Предприятия. Это связано с тем, как объекты HTTPСоединение и HTTPЗапрос в 1С:Предприятие обрабатывают базовую аутентификацию по сравнению с более гибким объектом КлиентHttp в 1С:Элемент или прямым формированием HTTP-запросов. Для проверки отправляемых заголовков и параметров непосредственно в 1С можно использовать инструмент для тестирования HTTP-запросов.

Для начала, давайте посмотрим на пример кода, который отлично работает для получения токена в 1С:Элемент:


@НаСервере
@ДоступноСКлиента
@ВПодсистеме метод ПолучитьТокен:Строка
исп Ответ = КлиентHttp.ЗапросPost("
    .ДобавитьЗаголовок("Content-Type", "application/x-www-form-urlencoded")
    .ДобавитьЗаголовок("Authorization", "Basic " + Кодировки.Base64.КодироватьВСтроку( "ВашClient-ID:ВашClient-Secret"))
    .УстановитьТело("grant_type=CLIENT_CREDENTIALS")
    .Выполнить
знч СтрокаОтвета = Ответ.Тело.ПрочитатьКакСтроку
Ответ.Закрыть
возврат СтрокаОтвета ;

Как мы видим, в этом примере заголовок Authorization формируется вручную, что является стандартной практикой для Basic-аутентификации в контексте OAuth 2.0 Client Credentials Flow. Однако, при попытке адаптировать этот подход для 1С:Предприятия, используя объекты HTTPСоединение и HTTPЗапрос, мы можем столкнуться с ошибкой. Рассмотрим код, который привел к "400 Bad Request":


Процедура ПолучитьBearerТокенPOSTНаСервере
    HTTPСоединение = Новый HTTPСоединение("app-613507.1cmycloud.com/applications/MyApp-dev", ,,,,, Новый ЗащищенноеСоединениеOpenSSL);
    Авторизация = "Basic " + Base64Строка(ПолучитьДвоичныеДанныеИзСтроки("ВашClient-ID:ВашClient-Secret"));
    ЗаголовкиЗапроса = Новый Соответствие;
    ЗаголовкиЗапроса.Вставить("Content-Type" , "application/x-www-form-urlencoded");
    ЗаголовкиЗапроса.Вставить("Authorization" , Авторизация); // Здесь дублирование
    HTTPЗапрос = Новый HTTPЗапрос("/sys/token", ЗаголовкиЗапроса);
    HTTPЗапрос.УстановитьТелоИзСтроки("grant_type=CLIENT_CREDENTIALS");
    HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
    Ответ = HTTPОтвет.ПолучитьТелоКакСтроку
КонецПроцедуры

Сервер, получая такой запрос, возвращает ошибку "400 Bad Request", что означает "некорректный запрос". Это может быть вызвано множеством причин, но в данном случае мы выяснили, что основная проблема кроется в способе обработки аутентификации в HTTPСоединение 1С:Предприятия.

Ключевые аспекты корректного выполнения HTTP-запроса в 1С:Предприятие

Чтобы успешно получить Bearer токен из 1С:Элемент, нам необходимо учесть несколько важных нюансов при работе с объектами HTTPСоединение и HTTPЗапрос в 1С:Предприятие. Разберем каждый шаг по порядку.

  1. Передача учетных данных в конструктор HTTPСоединение

    Одним из самых важных отличий является то, как 1С:Предприятие обрабатывает базовую аутентификацию. В отличие от прямого указания заголовка Authorization, как это было в примере для 1С:Элемент или в Postman, в 1С:Предприятие для объектов HTTPСоединение мы можем (и должны) передать логин (в нашем случае это Client-ID) и пароль (Client-Secret) непосредственно в конструктор объекта HTTPСоединение.

    Платформа 1С самостоятельно сформирует необходимый заголовок Authorization: Basic <Base64(Client-ID:Client-Secret)> и добавит его к исходящему запросу. Это является критически важным моментом для успешной аутентификации.

    Пример корректного создания HTTPСоединение:

    
    HTTPСоединение = Новый HTTPСоединение(
        "app-613507.1cmycloud.com", // Хост сервера 1С:Элемент
        "ВашClient-ID",             // Client-ID в качестве Пользователя
        "ВашClient-Secret",         // Client-Secret в качестве Пароля
        , , ,                       // Прочие параметры прокси, тайм-аута и т.д. (оставляем пустыми, если не нужны)
        Новый ЗащищенноеСоединениеOpenSSL // Для HTTPS соединения
    );
    

    Обратите внимание, что Client-ID и Client-Secret передаются как второй и третий параметры конструктора HTTPСоединение соответственно. Это значительно упрощает процесс, так как система берет на себя формирование корректного заголовка.

  2. Удаление ручного заголовка Authorization

    Как мы уже выяснили, когда учетные данные передаются в конструктор HTTPСоединение, система 1С самостоятельно формирует и добавляет заголовок Authorization. Если мы дополнительно попытаемся вручную добавить заголовок Authorization в объект HTTPЗапрос (как это было в изначальном нерабочем коде), это приведет к дублированию или некорректному форматированию заголовков в исходящем HTTP-запросе. Сервер 1С:Элемент воспримет такой запрос как недействительный и ответит ошибкой "400 Bad Request".

    Поэтому, крайне важно убедиться, что в коллекции ЗаголовкиЗапроса, которую мы передаем в HTTPЗапрос, не присутствует ключ "Authorization", если вы уже используете параметры Пользователь и Пароль в конструкторе HTTPСоединение.

  3. Корректное разделение URL

    Адрес ресурса, к которому мы обращаемся, должен быть правильно распределен между конструкторами HTTPСоединение и HTTPЗапрос. В HTTPСоединение следует передавать только хост (доменное имя или IP-адрес сервера). Полный относительный путь к ресурсу должен быть указан в конструкторе HTTPЗапрос.

    • В конструктор HTTPСоединение передается только хост, например: "app-613507.1cmycloud.com".
    • В конструктор HTTPЗапрос передается относительный путь к эндпоинту получения токена, например: "applications/Chiriker-dev/sys/token".

    Пример:

    
    HTTPСоединение = Новый HTTPСоединение("app-613507.1cmycloud.com", ...); // Только хост
    ...
    HTTPЗапрос = Новый HTTPЗапрос("applications/Chiriker-dev/sys/token", ЗаголовкиЗапроса); // Относительный путь
    
  4. Указание кодировки

    Для предотвращения потенциальных проблем с кодировкой данных, особенно при передаче тела запроса (grant_type=CLIENT_CREDENTIALS), рекомендуется явно указывать кодировку UTF-8. Это гарантирует, что сервер правильно интерпретирует символы в запросе.

    • Заголовок Content-Type должен содержать charset=utf-8:
    • 
      ЗаголовкиЗапроса.Вставить("Content-Type" , "application/x-www-form-urlencoded; charset=utf-8");
      
    • Метод УстановитьТелоИзСтроки должен принимать параметр КодировкаТекста.UTF8:
    • 
      HTTPЗапрос.УстановитьТелоИзСтроки("grant_type=CLIENT_CREDENTIALS", КодировкаТекста.UTF8);
      

    Явное указание кодировки является хорошей практикой и помогает избежать многих проблем, связанных с интерпретацией данных на разных системах.

Финальный рабочий код получения Bearer токена в 1С:Предприятие

Теперь, когда мы разобрались со всеми нюансами, давайте соберем весь рабочий код для получения Bearer токена из 1С:Элемент, который мы можем использовать в 1С:Предприятие. Этот код учитывает все вышеописанные рекомендации и позволяет избежать ошибки "400 Bad Request".


Процедура ПолучитьBearerТокенДля1СЭлемент()
    // 1. Параметры для аутентификации (Client-ID и Client-Secret)
    // Внимание: эти данные должны храниться в безопасном месте (например, в константах или параметрах системы),
    // а не быть жестко закодированы в реальных решениях.
    Перем КлиентID, КлиентСекрет;
    КлиентID = "kzNEwTSih7tBySSWGp7dDRTpg1qA1Dqo4cPOmDKwjSw="; // Замените на Ваш Client-ID
    КлиентСекрет = "QkGxDFcOxuBdQfA04fq1eVR-wxBuNI3TLEmMB7hSlK8="; // Замените на Ваш Client-Secret

    // 2. Создаем HTTPСоединение, передавая хост, Client-ID и Client-Secret.
    // 1С:Предприятие автоматически сформирует заголовок Authorization: Basic.
    Попытка
        HTTPСоединение = Новый HTTPСоединение(
            "app-613507.1cmycloud.com", // Хост сервера 1С:Элемент
            КлиентID,                 // Client-ID в качестве Пользователя
            КлиентСекрет,             // Client-Secret в качестве Пароля
            , , ,                       // Прочие параметры (прокси, тайм-аут и т.д.) - оставляем пустыми, если не нужны
            Новый ЗащищенноеСоединениеOpenSSL // Обязательно для HTTPS соединения
        );
    Исключение
        Сообщить("Ошибка при создании HTTPСоединения: " + ОписаниеОшибки());
        Возврат;
    КонецПопытки;

    // 3. Создаем ЗаголовкиЗапроса.
    ЗаголовкиЗапроса = Новый Соответствие;
    // Указываем Content-Type с кодировкой UTF-8 для тела запроса.
    ЗаголовкиЗапроса.Вставить("Content-Type" , "application/x-www-form-urlencoded; charset=utf-8");
    // Важно: заголовок "Authorization" НЕ добавляем вручную, так как он будет
    // автоматически сформирован и добавлен объектом HTTPСоединение на основе переданных ClientID и ClientSecret.

    // 4. Создаем HTTPЗапрос, указывая относительный путь к эндпоинту токена.
    // Путь "applications/Chiriker-dev/sys/token" - это пример, замените его на актуальный путь
    // к эндпоинту токена вашего приложения в 1С:Элемент.
    HTTPЗапрос = Новый HTTPЗапрос("applications/Chiriker-dev/sys/token", ЗаголовкиЗапроса);

    // 5. Устанавливаем тело запроса с типом grant_type=CLIENT_CREDENTIALS и кодировкой UTF-8.
    // Этот тип гранта используется для аутентификации серверных приложений.
    HTTPЗапрос.УстановитьТелоИзСтроки("grant_type=CLIENT_CREDENTIALS", КодировкаТекста.UTF8);

    // 6. Отправляем запрос.
    Попытка
        HTTPОтвет = HTTPСоединение.ВызватьHTTPМетод("POST", HTTPЗапрос);
    Исключение
        Сообщить("Ошибка при отправке HTTP-запроса: " + ОписаниеОшибки());
        Возврат;
    КонецПопытки;

    // 7. Обрабатываем полученный ответ от сервера 1С:Элемент.
    Если HTTPОтвет.КодСостояния = 200 Тогда
        // Запрос успешно выполнен, получаем тело ответа, которое содержит JSON с токеном.
        СтрокаОтвета = HTTPОтвет.ПолучитьТелоКакСтроку();
        Сообщить("Токен успешно получен: " + СтрокаОтвета);
        // Здесь вы можете разобрать JSON-ответ (например, с помощью JSON.Прочитать())
        // для извлечения access_token, expires_in и других полей.
        // Пример:
        // ЧтениеJSON = Новый ЧтениеJSON;
        // ЧтениеJSON.УстановитьСтроку(СтрокаОтвета);
        // ДанныеОтвета = JSON.Прочитать(ЧтениеJSON);
        // AccessToken = ДанныеОтвета.access_token;
        // Сообщить("Access Token: " + AccessToken);
        // ЧтениеJSON.Закрыть();
    Иначе
        // Произошла ошибка. Получаем подробности из тела ответа сервера.
        СтрокаОшибки = HTTPОтвет.ПолучитьТелоКакСтроку();
        Сообщить("Ошибка получения токена. Код состояния: " + HTTPОтвет.КодСостояния + ", Ответ сервера: " + СтрокаОшибки);
    КонецЕсли;

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

Почему именно такой подход работает?

Когда мы указываем Client-ID и Client-Secret в параметрах Пользователь и Пароль конструктора HTTPСоединение, платформа 1С:Предприятие автоматически формирует и включает заголовок Authorization: Basic <Base64(Client-ID:Client-Secret)> в исходящий HTTP-запрос. Этот механизм аутентификации является встроенным и предпочтительным способом работы с базовой аутентификацией для объекта HTTPСоединение.

Таким образом, сервер 1С:Элемент получает корректные данные для базовой аутентификации, которые он ожидает, и успешно выдает Bearer токен. Любые попытки вручную продублировать или некорректно сформировать заголовок Authorization при использовании этого механизма приводят к ошибке "400 Bad Request", так как сервер обнаруживает невалидный формат запроса.

Дополнительные рекомендации и устранение возможных проблем

Даже при правильной реализации кода, могут возникнуть другие причины, по которым запрос может не пройти успешно. Вот несколько моментов, на которые стоит обратить внимание:

  1. Права доступа в 1С:Элемент: Мы должны убедиться, что для пользователя (Client-ID), от имени которого выполняется запрос, разрешен вызов HTTP-сервисов в панели управления 1С:Элемент. Если в Postman токен получается, а в 1С:Предприятие нет (даже после исправления кода), возможно, есть специфические настройки безопасности, которые нужно проверить.
  2. Корректность URL-путей: Проверьте, что путь к эндпоинту токена, указанный в HTTPЗапрос (например, "applications/Chiriker-dev/sys/token"), абсолютно точно соответствует адресу в вашем экземпляре 1С:Элемент. Малейшая опечатка может привести к ошибке "400 Bad Request" или "404 Not Found".
  3. SSL/TLS сертификаты: Если вы работаете по HTTPS, убедитесь, что сертификаты сервера доверяются вашей системе 1С. В конструкторе HTTPСоединение мы используем Новый ЗащищенноеСоединениеOpenSSL, что является стандартным подходом, но проблемы с цепочкой доверия сертификатов все же могут возникнуть.
  4. Отладка с помощью Postman: Если вы столкнулись с новой проблемой, всегда полезно сначала протестировать запрос в Postman. Получив рабочий запрос в Postman, вы можете сравнить все заголовки и тело запроса с тем, что генерирует 1С:Предприятие, чтобы найти расхождения.
  5. Анализ ответа сервера: Если сервер возвращает ошибку, всегда старайтесь прочитать тело ответа. Часто оно содержит более детальное описание проблемы в формате JSON или XML, что значительно ускоряет поиск решения.

Следуя этим рекомендациям и используя представленный пример кода, вы сможете успешно интегрировать 1С:Предприятие с 1С:Элемент для получения Bearer токенов и избежать распространенных ошибок. Для дальнейшего развития интеграции между этими системами можно использовать готовое решение по созданию плана обмена для 1С:Элемент.

← На главную