При работе с распределенными информационными базами (РИБ) в конфигурациях на обычных формах, таких как 1С:Управление торговлей 10.3 или 1С:Комплексная автоматизация 1.1, администраторы и разработчики часто сталкиваются с неприятной циклической ошибкой. При входе пользователя в подчиненном или главном узле система выдает сообщение: «Пользователь "..." не найден в справочнике "Пользователи"». Если администратор вручную сопоставляет пользователя в одном узле, после очередного сеанса обмена данными связка ломается во втором узле, и ситуация повторяется бесконечно.
Разберем детально внутреннее устройство механизма авторизации в платформе 1С, выясним техническую причину конфликта и рассмотрим несколько проверенных способов решения данной проблемы.
Для понимания сути ошибки проанализируем, как платформа 1С организует учет пользователей. В системе сосуществуют две принципиально разные сущности:
v8users). Именно этот пользователь выбирается в окне авторизации при запуске 1С. Пользователи ИБ не мигрируют через стандартный механизм РИБ.Справочник.Пользователи. Это обычная таблица базы данных, которая хранит ФИО, контактную информацию, настройки отчетов и штатно передается между всеми узлами распределенной базы.Связь между системным пользователем и элементом справочника устанавливается по уникальному реквизиту ИдентификаторПользователяИБ (тип данных УникальныйИдентификатор / UUID). При создании пользователя ИБ платформа генерирует для него уникальный GUID. Важно помнить, что свойство УникальныйИдентификатор у объекта ПользовательИнформационнойБазы доступно только для чтения. Задать один и тот же GUID пользователям ИБ в разных узлах базы данных технически невозможно.
Когда вы создаете пользователя с одинаковым именем в центральном узле и в узле филиала, платформа присваивает им два разных идентификатора. Далее происходит следующий сценарий:
ИдентификаторПользователяИБ узла «А». Вход работает штатно.Пользователи передается в узел «Б» и перезаписывает реквизит ИдентификаторПользователяИБ значением из узла «А».Многие разработчики пробуют решить проблему автоматическим перепривязыванием пользователя при старте, однако пользователи без полных административных прав все равно не могут зайти. Выясним причину такого поведения:
ПользователиИнформационнойБазы.ПолучитьПользователей() и ПользователиИнформационнойБазы.НайтиПоИмени() требуют наличия административных прав. Рядовой пользователь вправе прочитать только свойства своего текущего сеанса (ПользователиИнформационнойБазы.ТекущийПользователь()), а любые глобальные поисковые вызовы без прав вызывают системное исключение «Нарушение прав доступа».УстановитьПривилегированныйРежим(Истина).РегистрСведений.НастройкиПользователей). Если у назначенной роли отсутствуют права на чтение или запись этих служебных регистров, вход прерывается ошибкой.Наиболее надежным решением является доработка процедуры сопоставления пользователя при начале работы системы. Алгоритм должен искать прикладного пользователя по имени (или коду), если сопоставление по GUID не дало результата, и автоматически обновлять ИдентификаторПользователяИБ прямо в текущем узле.
Рассмотрим реализацию данного механизма. В общем модуле УправлениеПользователями (или в модуле обычного приложения в процедуре ПриНачалеРаботыСистемы) добавим следующий обработчик:
Процедура ПроверитьИОбновитьСопоставлениеПользователя() Экспорт
// Включаем привилегированный режим для обхода ограничений прав
УстановитьПривилегированныйРежим(Истина);
ТекущийПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();
Если ТекущийПользовательИБ = Неопределено Тогда
Возврат;
КонецЕсли;
ИдентификаторТекущего = ТекущийПользовательИБ.УникальныйИдентификатор;
// Ищем прикладного пользователя по GUID
СсылкаПользователя = Справочники.Пользователи.НайтиПоРеквизиту("ИдентификаторПользователяИБ", ИдентификаторТекущего);
// Если по GUID не найден, ищем по полному или краткому наименованию
Если СсылкаПользователя.Пустая() Тогда
СсылкаПользователя = Справочники.Пользователи.НайтиПоНаименованию(ТекущийПользовательИБ.ПолноеИмя, Истина);
Если СсылкаПользователя.Пустая() Тогда
СсылкаПользователя = Справочники.Пользователи.НайтиПоРеквизиту("КраткоеИмя", ТекущийПользовательИБ.Имя);
КонецЕсли;
// Если прикладной пользователь найден по имени, связываем его с текущим пользователем ИБ
Если Не СсылкаПользователя.Пустая() Тогда
ОбъектПользователь = СсылкаПользователя.ПолучитьОбъект();
ОбъектПользователь.ИдентификаторПользователяИБ = ИдентификаторТекущего;
// Отключаем регистрацию изменений для обмена, чтобы не затирать GUID в других узлах
ОбъектПользователь.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
Попытка
ОбъектПользователь.Записать();
Исключение
ЗаписьЖурналаРегистрации("АвторизацияПользователя",
УровеньЖурналаРегистрации.Ошибка,,,
"Не удалось обновить идентификатор пользователя: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Обратите внимание на строку ОбъектПользователь.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;. Она гарантирует, что обновленный локальный GUID не будет зарегистрирован к отправке в другие узлы РИБ и не сломает их настройки при следующем обмене.
Если нет желания менять стартовую процедуру авторизации, можно настроить правила обмена так, чтобы значение реквизита ИдентификаторПользователяИБ не перезаписывалось при синхронизации.
Разберем, как это сделать через обработчики планов обмена в модуле объекта плана обмена (например, ПланОбмена.ПоУзлам):
ПриОтправкеДанныхПодчиненному (а также ПриОтправкеДанныхГлавному).Пользователи очищаем передачу реквизита либо обрабатываем его на стороне приемника в событии ПриПолученииДанныхОтПодчиненного / ПриПолученииДанныхОтГлавного:
Процедура ПриПолученииДанныхОтГлавного(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад)
Если ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.Пользователи") Тогда
// Если объект уже существует в текущей базе, сохраняем локальный идентификатор
СуществующаяСсылка = Справочники.Пользователи.НайтиПоНаименованию(ЭлементДанных.Наименование, Истина);
Если Не СуществующаяСсылка.Пустая() Тогда
ЭлементДанных.ИдентификаторПользователяИБ = СуществующаяСсылка.ИдентификаторПользователяИБ;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Благодаря этому подходу общие реквизиты справочника (контакты, роли, подразделения) продолжат мигрировать между узлами РИБ, а системная связка с пользователем ИБ останется уникальной для каждой базы данных.
В организациях с небольшим штатом и редкой сменой сотрудников часто применяют полное исключение справочника Пользователи из состава плана обмена. В этом случае пользователи заводятся в каждой базе данных независимо.
Важное предостережение: если вы исключаете справочник из плана обмена, необходимо обеспечить идентичность системных ссылок (GUID объектов базы данных) между узлами. Если в узле филиала создать документ, в реквизите Ответственный которого указан локально созданный пользователь, то при выгрузке документа в центральную базу реквизит превратится в «<Объект не найден>». Чтобы избежать этого, создавайте элементы справочника с помощью обработки с явным указанием фиксированного GUID ссылки.
Проанализировав все три варианта, наиболее стабильным и гибким решением для промышленных систем является Способ 1 — программный поиск по наименованию и установка локального идентификатора в привилегированном режиме.