При интеграции 1С с внешними платформами (например, сервисами доставки, маркетплейсами или платежными шлюзами) разработчики часто сталкиваются со специфическим требованием к протоколу авторизации. Внешняя система требует наличия единого базового адреса для всех запросов, но при этом протокол разделен на два этапа:
security/oauth/token), на который внешний сервис отправляет логин и пароль в теле сообщения или специальных параметрах без передачи стандартных заголовков HTTP-аутентификации. Этот эндпоинт должен быть доступен веб-серверу без запроса логина и пароля пользователя 1С.Authorization: Bearer) и должны жестко контролироваться платформой или кодом конфигурации.Выясним причину возникающей сложности. Параметры подключения к информационной базе в файле публикации default.vrd задаются глобально в строке подключения атрибута ib корневого элемента point. Если мы прописываем туда служебного пользователя через параметры Usr и Pwd, то все без исключения сервисы опубликованной базы перестают требовать аутентификацию на уровне веб-сервера. Если же убрать пользователя из default.vrd, модуль расширения веб-сервера 1С будет возвращать ошибку 401 Unauthorized еще до того, как управление будет передано в код метода HTTP-сервиса. Рассмотрим подробнее, какими способами можно обойти это ограничение и сохранить единый корень адреса публикации.
Разберем по шагам наиболее практичный способ решения задачи средствами веб-сервера Apache без привлечения дополнительного программного обеспечения. Идея заключается в публикации двух каталогов с независимыми файлами default.vrd и сопоставлении их с URL с помощью директив Alias в файле конфигурации httpd.conf.
Для реализации этого подхода создадим два каталога публикации на сервере:
C:/inetpub/wwwroot/MyBase), где файл default.vrd содержит строку подключения без логина и пароля Usr и Pwd.C:/inetpub/wwwroot/MyBase_Auth), где в файле default.vrd явно указаны служебный пользователь и пароль информационной базы.Проанализируем работу директивы Alias в Apache. Модуль сопоставления путей просматривает список правил сверху вниз и применяет первое совпавшее правило. Поэтому критически важно соблюдать правильный порядок: более длинный, специфичный URL должен располагаться выше, чем общий базовый путь базы.
Посмотрим на пример фрагмента конфигурации httpd.conf:
# 1. Специфичный путь для точки получения токена (ведет к default.vrd с логином и паролем)
Alias "/MyBase/hs/YandexEda/security/oauth" "C:/inetpub/wwwroot/MyBase_Auth"
<Directory "C:/inetpub/wwwroot/MyBase_Auth">
AllowOverride None
Options None
Require all granted
SetHandler 1c-application
</Directory>
# 2. Общий базовый путь для всех остальных запросов (ведет к default.vrd без параметров Usr/Pwd)
Alias "/MyBase" "C:/inetpub/wwwroot/MyBase"
<Directory "C:/inetpub/wwwroot/MyBase">
AllowOverride None
Options None
Require all granted
SetHandler 1c-application
</Directory>
Когда внешний сервис обращается по адресу /MyBase/hs/YandexEda/security/oauth/token, Apache сопоставляет запрос с первым правилом и обращается к файлу default.vrd из каталога MyBase_Auth. Пользователь входит в 1С под служебной учетной записью, отрабатывает код метода и возвращает токен. Когда же приходит запрос на адрес /MyBase/hs/YandexEda/nomenclature/composition, первое правило не срабатывает, запрос перенаправляется во вторую директорию и обрабатывается стандартными механизмами проверки прав.
Важное замечание: Обратите внимание на значение атрибута base в файле default.vrd авторизационной директории. Модуль 1С сопоставляет входящие шаблоны HTTP-сервисов относительно значения base. Чтобы маршрутизация внутри 1С не возвращала ошибку 404, значение base должно соответствовать алиасу, назначенному в веб-сервере.
Если нет желания создавать две отдельные публикации и дублировать каталоги, рассмотрим изящный альтернативный способ: автоматическое внедрение учетных данных веб-сервером через модуль mod_headers.
В этом сценарии информационная база публикуется один раз без сохранения пароля в default.vrd. Для запросов к точке авторизации Apache сам добавляет заголовок Basic-аутентификации перед передачей запроса модулю 1С. Разберем по шагам настройку:
httpd.conf включен модуль LoadModule headers_module modules/mod_headers.so.ServiceUser:SecretPassword закодированная строка будет иметь вид U2VydmljZVVzZXI6U2VjcmV0UGFzc3dvcmQ=.<Location> только для адреса получения токена.Посмотрим на пример конфигурации:
<Location "/MyBase/hs/YandexEda/security/oauth">
RequestHeader set Authorization "Basic U2VydmljZVVzZXI6U2VjcmV0UGFzc3dvcmQ="
</Location>
В этом случае внешний клиент отправляет чистый запрос без заголовков авторизации, Apache на лету внедряет заголовок Authorization, платформа 1С успешно авторизует вызов под служебной учетной записью, а все прочие адреса сервиса остаются защищенными.
В архитектуре корпоративных систем часто используется подход с выносом маршрутизации на обратный прокси-сервер (Nginx или OpenResty), расположенный перед Apache или внутренним веб-сервером. Это позволяет гибко настраивать адресацию, отрезать служебные префиксы /hs/ и терминировать SSL-сертификаты.
При таком подходе в 1С создаются две обычные независимые публикации с разными именами, например /MyBase_Public и /MyBase_Auth, а внешний единый интерфейс конфигурируется в Nginx:
server {
listen 443 ssl;
server_name mysite.ru;
# Маршрутизация метода получения токена на открытую публикацию
location /MyBase/hs/YandexEda/security/oauth {
proxy_pass http://127.0.0.1:8080/MyBase_Auth/hs/YandexEda/security/oauth;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Маршрутизация остальных вызовов на защищенную публикацию
location /MyBase/ {
proxy_pass http://127.0.0.1:8080/MyBase_Public/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Начиная с версии платформы 8.3.21, в 1С реализован нативный механизм валидации токенов доступа на уровне веб-сервера. Это избавляет от необходимости вручную парсить токены в каждом обработчике или создавать сложные схемы с раздельными публикациями.
Проанализируем порядок настройки штатного механизма:
default.vrd внутри описания сервиса определяем параметры проверки токена с помощью блока accessTokenAuthentication, указав имя сервиса в качестве получателя и параметры доверенного эмитента:
<httpServices>
<service name="YandexEda">
<accessTokenAuthentication>
<accessTokenRecepientName>YandexEdaService</accessTokenRecepientName>
<issuers>
<issuer issuerName="My1CAuthServer"
authenticationClaimName="sub"
authenticationUserPropertyName="name">
<keyInformation>СекретныйКлючПодписи</keyInformation>
</issuer>
</issuers>
</accessTokenAuthentication>
</service>
</httpServices>
ТокенДоступа.Посмотрим на пример генерации токена в коде 1С:
Токен = Новый ТокенДоступа;
Токен.Эмитент = "My1CAuthServer";
Токен.Получатели.Добавить("YandexEdaService");
Токен.ИмяПользователя = "ИнтеграцияЯндекс";
Токен.ВремяЖизни = 3600; // 1 час
СекретныйКлюч = Новый ЗащищенноеХранилищеОписанияКлюча("СекретныйКлючПодписи");
СтрокаТокена = Токен.Подписать(АлгоритмПодписиТокенаДоступа.HS256, СекретныйКлюч);
При таком подходе метод получения токена может быть открытым, а проверка валидности входящих запросов с заголовком Authorization: Bearer выполняется веб-сервером автоматически еще до обращения к прикладному коду.
При использовании анонимных публикаций или автоматической подстановки служебного пользователя важно правильно настроить повторное использование сеансов в default.vrd. По умолчанию каждый входящий HTTP-запрос может инициировать запуск нового фонового сеанса, что быстро приведет к исчерпанию клиентских лицензий 1С.
Чтобы избежать захвата лишних лицензий, в элемент point файла default.vrd необходимо добавить параметры пула сеансов:
<point xmlns="http://v8.1c.ru/8.2/virtual-resource-system"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
base="/MyBase"
ib="Srvr=Server1C;Ref=MyBase;"
reuseSessions="autouse"
sessionMaxAge="60"
poolSize="10">
Атрибут reuseSessions="autouse" разрешает платформе повторно использовать освободившиеся сеансы того же пользователя, poolSize ограничивает максимальное число одновременно удерживаемых сеансов, а sessionMaxAge определяет время их удержания в секундах при отсутствии входящих запросов.