При интеграции «1С:Предприятия» с внешними системами (мобильными приложениями, сайтами, личными кабинетами) через веб-сервисы (SOAP) или HTTP-сервисы часто возникает задача ведения истории изменений. Автор темы столкнулся с классической проблемой: при записи объектов через веб-сервис стандартная подсистема версионирования БСП (Библиотеки Стандартных Подсистем) записывает изменения на одного системного пользователя, под которым опубликована база. Нам нужно разобраться, как сделать так, чтобы система видела реального инициатора изменений, и при этом избежать масштабного переписывания существующего кода.
В этой статье мы подробно разберем механизмы авторизации в веб-сервисах 1С, выясним, как платформа определяет текущего пользователя, и рассмотрим эффективные архитектурные решения, включая «лайфхак» с динамической подменой параметров сеанса.
Когда внешний клиент обращается к веб-сервису или HTTP-сервису, платформа запускает полноценный сеанс 1С. То, какого пользователя вернет обращение к ПараметрыСеанса.ТекущийПользователь или вызов функции БСП Пользователи.АвторизованныйПользователь(), целиком и полностью зависит от настроек авторизации на веб-сервере (IIS или Apache) и в файле публикации default.vrd.
Рассмотрим два основных сценария авторизации:
default.vrd в строке подключения к базе данных (параметр ib) жестко указаны имя пользователя и пароль (например, usr=WebServiceUser;pwd=SecretPassword;), то все без исключения вызовы со всех внешних устройств будут выполняться в контексте этого одного системного пользователя. В этом случае 1С «из коробки» не сможет узнать, кто именно нажал кнопку на стороне сайта или мобильного приложения.Authorization при каждом запросе. Если у каждого физического пользователя есть своя учетная запись в справочнике «Пользователи» 1С, и внешняя система передает именно его логин и пароль, то сеанс будет запущен под конкретным пользователем. При этом стандартное версионирование и Журнал регистрации будут работать автоматически, фиксируя действия реального человека без написания дополнительного кода.Однако на практике использовать индивидуальную базовую авторизацию для сотен мобильных клиентов небезопасно и неудобно в администрировании. Ниже мы разберем альтернативные варианты решения этой задачи.
Если мы хотим оставить единую системную учетную запись для авторизации на веб-сервере, но при этом разделять авторов изменений в отчетах по версионированию, мы можем использовать программную подмену параметров сеанса. Это позволит нам обойтись «парой строчек кода» в самом сервисе и не менять сигнатуры бизнес-методов.
Идея заключается в следующем: внешнее приложение передает уникальный идентификатор или имя реального пользователя в HTTP-заголовках запроса. При обработке запроса в 1С мы считываем этот заголовок и принудительно перезаписываем значение параметра сеанса ТекущийПользователь.
Давайте разберем по шагам, как это реализовать:
Шаг 1. Передача заголовка со стороны клиента. Внешний софт при отправке HTTP-запроса добавляет кастомный заголовок, например, X-Current-Worker, в котором передает имя или GUID пользователя. Для проверки таких запросов подойдёт обработка для тестирования HTTP-сервисов с возможностью редактирования заголовков.
Шаг 2. Перехват и подмена в модуле сервиса. В самом начале выполнения метода нашего сервиса мы выполняем переинициализацию параметра сеанса. Проанализируем следующий пример кода:
// Пример для HTTP-сервиса
Функция ВыполнитьИмпортДанных(Запрос)
// Получаем имя реального пользователя из кастомного заголовка
ИмяРеальногоПользователя = Запрос.Заголовки.Получить("X-Current-Worker");
Если ЗначениеЗаполнено(ИмяРеальногоПользователя) Тогда
// Ищем пользователя в справочнике "Пользователи"
НайденныйПользователь = Справочники.Пользователи.НайтиПоНаименованию(ИмяРеальногоПользователя);
Если ЗначениеЗаполнено(НайденныйПользователь) Тогда
// Устанавливаем привилегированный режим для записи параметра сеанса
УстановитьПривилегированныйРежим(Истина);
// Подменяем текущего пользователя для контекста выполняющегося запроса
ПараметрыСеанса.ТекущийПользователь = НайденныйПользователь;
УстановитьПривилегированныйРежим(Ложь);
КонецЕсли;
КонецЕсли;
// Далее следует основной прикладной код создания или изменения объектов
// Любые подписки на события (включая БСП версионирование) запишут объект от имени НайденныйПользователь
Возврат Новый HTTPСервисОтвет(200);
КонецФункции
Благодаря этому решению стандартный механизм версионирования БСП, который срабатывает в подписке на событие ПередЗаписью или ПриЗаписи объектов и считывает данные из ПараметрыСеанса.ТекущийПользователь, зафиксирует изменения под именем реального сотрудника.
Если мы используем классический Web-сервис (SOAP), работающий по строго описанной WSDL-схеме, мы можем добавить дополнительный входящий параметр во все экспортируемые методы. Например, параметр UserLogin типа string.
Рассмотрим пример реализации метода SOAP-сервиса с явным поиском и установкой пользователя:
Функция ЗаписатьДокументПродажи(ДанныеДокумента, ИмяПользователя) Экспорт
// Инициализируем пользователя перед началом транзакции записи
Если ЗначениеЗаполнено(ИмяПользователя) Тогда
ПользовательСсылка = Справочники.Пользователи.НайтиПоРеквизиту("ИдентификаторДляВнешнихСистем", ИмяПользователя);
Если ЗначениеЗаполнено(ПользовательСсылка) Тогда
УстановитьПривилегированныйРежим(Истина);
ПараметрыСеанса.ТекущийПользователь = ПользовательСсылка;
УстановитьПривилегированныйРежим(Ложь);
КонецЕсли;
КонецЕсли;
// Логика создания и проведения документа
// ...
Возврат Истина;
КонецФункции
Этот способ надежен, но требует модификации WSDL-схемы сервиса и обновления интеграционного кода на стороне всех внешних потребителей, что не всегда возможно или желательно.
При работе с веб-сервисами крайне важно учитывать такой механизм платформы 1С, как пул соединений (Session Pooling). В файле настроек публикации default.vrd параметры пула (такие как reuseSessions или poolSize) определяют, что сеансы после выполнения запроса не уничтожаются сразу, а засыпают и возвращаются в пул для повторного использования последующими запросами.
В чем заключается опасность при подмене параметров сеанса?
Если Запрос №1 пришел от Пользователя А, и мы программно записали в ПараметрыСеанса.ТекущийПользователь ссылку на Пользователя А, то после завершения работы запроса сеанс вернется в пул. Если следующий Запрос №2 от Пользователя Б попадет в этот же повторно используемый сеанс и по какой-то причине не передаст заголовок идентификации (или произойдет сбой чтения), то в параметре сеанса ТекущийПользователь останется «грязное» значение от предыдущего вызова — то есть Пользователь А.
Чтобы избежать подобных ошибок, проанализируем и внедрим следующие правила разработки:
ПараметрыСеанса.ТекущийПользователь на дефолтного системного пользователя.УстановкаПараметровСеанса в Модуле сеанса для гарантированного контроля инициализации параметров при старте и переиспользовании сеанса веб-сервиса.Пример безопасного сброса параметров сеанса в обработчике запроса:
// Безопасный сброс параметров в начале обработки любого запроса
УстановитьПривилегированныйРежим(Истина);
// Сначала сбрасываем на дефолтного пользователя интеграции
ПараметрыСеанса.ТекущийПользователь = Справочники.Пользователи.НайтиПоРеквизиту("ИмяВхода", "SystemWebServiceUser");
УстановитьПривилегированныйРежим(Ложь);
// И только потом пытаемся прочитать переданного в заголовке реального пользователя
// ... код чтения заголовка и поиска пользователя ...
Используя предложенные методики, вы сможете легко и элегантно подружить механизмы интеграции через веб-сервисы со стандартной подсистемой версионирования БСП без необходимости переписывать бизнес-логику конфигурации.