В практике разработки на платформе 1С:Предприятие часто возникают задачи, связанные с интеграцией системы и окружения пользователя. Одной из таких задач является идентификация конкретного сотрудника не по его учетной записи в базе 1С, а по его доменному имени или локальному пользователю операционной системы Windows. Особенно актуален этот вопрос при использовании терминальных серверов (RDP), где несколько пользователей работают на одном физическом или виртуальном сервере, но под разными сеансами ОС.
В этой статье мы подробно разберем, как программно определить логин пользователя Windows в момент запуска клиентского приложения 1С, почему стандартные методы сервера здесь не работают, и как правильно спроектировать архитектуру решения для клиент-серверного варианта работы.
Представим ситуацию: у нас есть клиент-серверная база 1С. Пользователи подключаются к ней через терминальный сервер (Remote Desktop). Нам необходимо в момент старта системы выяснить:
Стандартная функция платформы ИмяПользователя() возвращает имя пользователя, заведенного в справочнике "Пользователи" самой 1С. Это нам не подходит, так как нам нужна привязка именно к системному логину (например, для автоматического определения прав, заполнения настроек по умолчанию или аудита безопасности).
Также важно понимать различие контекстов:
USR1CV8 или LocalSystem). Любая попытка узнать имя пользователя средствами ОС на сервере вернет имя этой служебной учетной записи.Самым надежным и распространенным способом получения сведений о сетевом окружении в среде Windows является использование системного объекта WScript.Network. Этот объект предоставляет доступ к данным о сети, имени компьютера и имени текущего пользователя.
Поскольку работа с COM-объектами доступна на клиенте (в тонком или толстом клиенте под Windows), мы можем использовать следующий код. Разберем его подробно.
Для начала создадим экземпляр COM-объекта:
// Этот код должен выполняться НА КЛИЕНТЕ (Тонкий клиент, Толстый клиент)
Попытка
СетеваяОболочка = Новый COMОбъект("WScript.Network");
ИмяПользователяОС = СетеваяОболочка.UserName;
ДоменПользователя = СетеваяОболочка.UserDomain;
ИмяКомпьютера = СетеваяОболочка.ComputerName;
Сообщить("Пользователь ОС: " + ИмяПользователяОС);
Сообщить("Домен: " + ДоменПользователя);
Исключение
// Обработка ситуации, если создание COM-объекта запрещено политиками безопасности
// или если клиент запущен не под Windows (например, Linux или MacOS)
ОписаниеОшибки = ОписаниеОшибки();
ЗаписьЖурналаРегистрации("ПолучениеИмениПользователяОС",
УровеньЖурналаРегистрации.Ошибка, , ,
"Не удалось создать WScript.Network: " + ОписаниеОшибки);
КонецПопытки;
Важные замечания по этому методу:
Попытка...Исключение обязательно.В ходе обсуждения был предложен альтернативный вариант — "хак" через анализ пути к временным файлам. Давайте проанализируем этот метод и поймем, почему его стоит использовать с осторожностью.
В Windows путь к временной папке пользователя обычно выглядит так: C:\Users\ИмяПользователя\AppData\Local\Temp\. Идея заключается в том, чтобы получить этот путь средствами 1С и "вырезать" из него имя пользователя.
// Пример логики (не рекомендуется для продакшена без проверок)
ПолныйПуть = КаталогВременныхФайлов(); // Например: "C:\Users\Serg_1960\AppData\Local\Temp\"
МассивЧастей = СтрРазделить(ПолныйПуть, "\");
// В стандартной структуре Windows имя пользователя часто находится на определенной позиции
// Но это ненадежно, так как структура может меняться.
Почему этот метод ненадежен:
Documents and Settings).Temp\2).Поэтому мы рекомендуем использовать первый метод с WScript.Network как основной.
Самая сложная часть задачи — это не просто получить имя, а сделать его доступным в глобальном контексте приложения, чтобы использовать в отчетах, документах или правах доступа. Так как код WScript.Network работает только на клиенте, а основные алгоритмы выполняются на сервере, нам нужно передать это значение.
Рассмотрим правильный паттерн реализации через Параметры Сеанса.
В дереве конфигурации создадим новый Параметр сеанса, назовем его ТекущийПользовательОС. Тип значения — Строка (длина 50-100 символов).
Создадим Общий модуль (или используем существующий), у которого установлены флаги "Сервер" и "Вызов сервера". Назовем его, например, УправлениеСеансомСервер.
// Общий модуль: УправлениеСеансомСервер
// Флажки: Сервер, Вызов сервера
Процедура УстановитьИмяПользователяОС(Знач ИмяПользователя) Экспорт
// Проверка прав доступа к параметрам сеанса может потребоваться, если используется RLS
ПараметрыСеанса.ТекущийПользовательОС = ИмяПользователя;
КонецПроцедуры
В модуле управляемого приложения (файл ManagedApplicationModule) используем событие ПриНачалеРаботыСистемы или ПередНачаломРаботыСистемы. Именно здесь мы получаем данные и сразу отправляем их на сервер.
// Модуль управляемого приложения
Процедура ПриНачалеРаботыСистемы()
// Получаем имя пользователя Windows
ИмяUserOS = "";
#Если ВебКлиент Тогда
// Для веб-клиента логика должна быть другой (например, через расширение браузера)
// В данном примере пропускаем
#ИначеЕсли МобильныйКлиент Тогда
// Для мобильного клиента WScript недоступен
#Иначе
// Тонкий клиент / Толстый клиент (Windows)
Попытка
СетеваяОболочка = Новый COMОбъект("WScript.Network");
ИмяUserOS = СетеваяОболочка.UserName;
Исключение
// Логирование ошибки
КонецПопытки;
#КонецЕсли
// Если удалось определить имя, отправляем его на сервер
Если ЗначениеЗаполнено(ИмяUserOS) Тогда
УправлениеСеансомСервер.УстановитьИмяПользователяОС(ИмяUserOS);
КонецЕсли;
КонецПроцедуры
Автор вопроса упоминал работу в терминальной сессии. Разберем нюансы, которые могут возникнуть при использовании описанного метода.
При использовании WScript.Network:
UserName вернет именно того пользователя, который залогинился в удаленный рабочий стол. Это то, что нам нужно.ComputerName вернет сетевое имя терминального сервера, а не имя локального компьютера, с которого пользователь подключился.Если вам дополнительно потребуется узнать имя клиентской станции, с которой пришло RDP-подключение (например, чтобы знать, что бухгалтер зашел из дома, а не из офиса), использование WScript.Network.ComputerName введет вас в заблуждение — у всех пользователей оно будет одинаковым (имя сервера).
Чтобы узнать настоящее имя клиентского ПК (откуда идет подключение), необходимо читать переменную среды CLIENTNAME. Это также делается через COM-объект, но другой — WScript.Shell.
// Получение имени клиентской станции в RDP-сессии
Попытка
WshShell = Новый COMОбъект("WScript.Shell");
// Читаем переменную окружения
ПроцессОкружения = WshShell.Environment("Process");
ИмяКлиентаRDP = ПроцессОкружения.Item("CLIENTNAME");
Если ЗначениеЗаполнено(ИмяКлиентаRDP) Тогда
Сообщить("Подключение с компьютера: " + ИмяКлиентаRDP);
Иначе
Сообщить("Это локальная сессия (не RDP) или переменная недоступна.");
КонецЕсли;
Исключение
Сообщить("Не удалось получить переменные окружения.");
КонецПопытки;
UserName. Это наиболее стабильный метод.Попытка...Исключение.Temp, AppData) — это ненадежно и зависит от настроек конкретной ОС.