При работе с внешними сервисами через 1С:Линк или при специфической настройке Nginx/Apache разработчики часто сталкиваются с ситуацией "двойного барьера". Суть проблемы заключается в том, что сначала запрос должен пройти авторизацию на веб-сервере (дополнительная авторизация 1С:Линк), а затем — авторизацию в самой информационной базе 1С. Рассмотрим подробнее, почему стандартный подход с объектом HTTPСоединение выдает ошибку 401 Unauthorized, и как мы можем решить эту задачу.
Проанализируем техническую сторону вопроса. Согласно стандарту HTTP, для передачи учетных данных используется заголовок Authorization с типом Basic. Когда мы создаем объект HTTPСоединение и указываем логин и пароль в параметрах конструктора, платформа 1С автоматически формирует этот заголовок.
Однако если мы пытаемся добавить второй логин и пароль (для веб-сервера) вручную через соответствие заголовков HTTPЗапрос, возникает конфликт. В одном запросе не может быть двух заголовков Authorization. Веб-сервер (например, Nginx) перехватывает заголовок, проверяет его и, если данные верны, пропускает запрос дальше. Но до самой 1С этот заголовок доходит либо в неизменном виде (и тогда 1С отклоняет его, так как пароль от веб-сервера не совпадает с паролем пользователя базы), либо заголовок перезаписывается, и его отклоняет уже веб-сервер.
Выясним причину ошибки 401 в ситуации автора: браузер при обращении к ресурсу умеет последовательно запрашивать данные (сначала для прокси/сервера, затем для приложения), сохраняя их в кэше и куки. Объект HTTPСоединение в 1С работает иначе — он stateless (не сохраняет состояние) и отправляет заголовки "как есть" в рамках одного вызова.
Если вы используете 1С:Линк, самым простым и правильным решением для программного доступа (через HTTP-сервисы) будет отключение "Дополнительной авторизации" в панели управления сервисом. Рассмотрим, почему это безопасно:
В этом случае код подключения будет стандартным и максимально надежным:
Соединение = Новый HTTPСоединение("example.link.1c.ru", 443, "Логин1С", "Пароль1С", , 30, Новый ЗащищенноеСоединениеOpenSSL());
Запрос = Новый HTTPЗапрос("/buh/hs/billing/version/");
Ответ = Соединение.Получить(Запрос);
Разберем ситуацию, когда авторизацию на веб-сервере убрать нельзя (например, этого требуют корпоративные стандарты), но мы можем управлять публикацией базы. В файле default.vrd можно жестко прописать учетные данные, под которыми веб-сервер будет подключаться к базе 1С.
Посмотрим на пример структуры строки подключения в файле .vrd:
<point xmlns="http://v8.1c.ru/8.2/virtual-resource-system"
base="/MyBase"
ib="Srvr='ServerName';Ref='BaseName';usr='ServiceUser';pwd='ServicePassword';">
</point>
В этом сценарии при выполнении HTTP-запроса вам нужно будет передать в заголовках только пароль от веб-сервера. Платформа 1С сама авторизует сеанс под пользователем ServiceUser, указанным в настройках публикации. Однако стоит помнить, что этот метод менее гибкий, так как все запросы будут выполняться от имени одного пользователя системы.
Проанализируем продвинутый вариант, который потребует доработки принимающей стороны (HTTP-сервиса в 1С). Если нам критически важно передавать оба набора данных, мы можем использовать стандартный заголовок для веб-сервера и собственный заголовок для 1С.
Рассмотрим шаги реализации:
HTTPСоединение указываем данные веб-сервера.X-1C-Auth.ГЕТ() или ПОСТ() мы программно считываем заголовок X-1C-Auth, декодируем его и проверяем права доступа.Пример кода для отправки такого запроса:
// Данные для веб-сервера (пройдут через Authorization)
Соединение = Новый HTTPСоединение(АдресСервера, 443, "UserWeb", "PassWeb", , , Новый ЗащищенноеСоединениеOpenSSL());
// Кодируем данные пользователя 1С в Base64 для передачи в своем заголовке
ДанныеАвторизации1С = "User1C:Pass1C";
СтрокаBase64 = ПолучитьBase64СтрокуИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзСтроки(ДанныеАвторизации1С, КодировкаТекста.UTF8, Ложь));
СтрокаBase64 = СтрЗаменить(СтрЗаменить(СтрокаBase64, Символы.ПС, ""), Символы.ВК, "");
Заголовки = Новый Соответствие;
Заголовки.Вставить("X-1C-Auth", СтрокаBase64);
Запрос = Новый HTTPЗапрос("/hs/my_api/v1/", Заголовки);
Ответ = Соединение.Получить(Запрос);
Важный нюанс: при формировании строки Base64 обязательно используйте удаление символов переноса строк (Символы.ПС и Символы.ВК). Платформа 1С при кодировании длинных строк может добавлять их автоматически, что приведет к ошибке 400 Bad Request или некорректной интерпретации заголовка на сервере.
Если вы все еще получаете 401, рекомендуем выполнить следующие действия:
Server или WWW-Authenticate ответа обычно указано, кто запрашивает авторизацию).Basic Auth часто приводит к ошибкам кодировки, которые крайне сложно отловить в коде 1С.Таким образом, мы выяснили, что "красивого" решения с передачей двух Basic паролей в одном HTTPСоединение не существует из-за ограничений самого протокола. Оптимальным путем является либо упрощение архитектуры (отключение одного уровня авторизации), либо использование программной проверки прав внутри самого сервиса.