Настройка аутентификации в 1С с использованием стандарта RFC 7519 (JWT)

27.02.24

Администрирование - Информационная безопасность

Рассмотрим в статье более подробную и последовательную настройку аутентификации в 1С с использованием распространенной технологии JWT, которая пришла в программу в платформе версии 8.3.21.1302.

В нашей конфигурации создадим HTTP-сервис.

 


Корневой URL у сервиса будет «auth».

 

.


Создаем шаблон URL с адресом «/service/». Создаем метод POST. Заполняем его свойства и делаем ему обработчик.

 


В базе данных создаем нового пользователя - AccessTokenGenerationUser. Задаем ему пароль «K3QvQz2y». Пользователь будет иметь свою отдельную роль с правами на вход в 1С и на наш POST-метод в HTTP-сервисе.

 

 

Далее создаем еще одного пользователя, под которым мы уже авторизуемся по JWT и получим доступ ко всем нашим сервисам в дальнейшем. Назовем его PublishingDatabaseService. В моем примере данный пользователь имеет полный доступ к взаимодействию со всеми сервисами, включая сервис с именем «Clients». При авторизации по определенному токену в сервис 1С мы авторизуемся как этот пользователь.

Модуль нашего сервиса аутентификации вместе с обработчиком выглядит следующим образом

 
 
#Область ОбработкаЗапросов

Функция СервисПолучитьТокен(HTTPЗапрос)
	
	ТелоHTTPОтвета = НовоеТелоHTTPОтвета();
	
	АвторизацияПоТокенамВключена = ПолучитьФункциональнуюОпцию("ЛМС_ИспользоватьТокеныДоступаДляАвторизацииСВнешнимиРесурсами");
	Если Не АвторизацияПоТокенамВключена Тогда
		
		ТелоHTTPОтвета.error = НСтр("ru = 'Авторизация по токенам JWT отключена.'", ОбщегоНазначения.КодОсновногоЯзыка()); 
		Возврат HTTPОтвет(ТелоHTTPОтвета, 500);
		
	КонецЕсли;
	
	ТелоЗапроса = HTTPЗапрос.ПолучитьТелоКакСтроку();
	ПараметрыЗапроса = ПрочитатьДанныеJSON(ТелоЗапроса);
	
	ИмяПолучателяТокена = Неопределено;
	ПараметрыЗапроса.Свойство("AccessTokenRecepientName", ИмяПолучателяТокена);
	
	Если ИмяПолучателяТокена = Неопределено Тогда
		
		ТелоHTTPОтвета.error = НСтр("ru = 'Получатель токена не найден.'", ОбщегоНазначения.КодОсновногоЯзыка()); 
		Возврат HTTPОтвет(ТелоHTTPОтвета, 500);
		
	КонецЕсли;
	
	КлючПодписи = КлючПодписиТокенаДоступа();
	
	ТокенДоступа = ТокенДоступаКВебСервису(ИмяПолучателяТокена, КлючПодписи);
	ТелоHTTPОтвета.AccessToken = ТокенДоступа;
	
	Возврат HTTPОтвет(ТелоHTTPОтвета);
	
КонецФункции 

#КонецОбласти // ОбработкаЗапросов

#Область СлужебныеПроцедурыИФункции

Функция НовоеТелоHTTPОтвета()
	
	Возврат Новый Структура("error, accessToken", "", Неопределено);
	
КонецФункции

Функция ТокенДоступаКВебСервису(ИмяПолучателяТокена, КлючПодписи)
	
	ТокенДоступа = Новый ТокенДоступа;
	ТокенДоступа.Заголовки.Вставить("alg", "HS256");
	
	ТокенДоступа.Эмитент                       = "ssl";
	ТокенДоступа.Получатели                    = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(ИмяПолучателяТокена);
	ТокенДоступа.КлючСопоставленияПользователя = "Web_Service";
	ТокенДоступа.ВремяСоздания                 = ТекущаяУниверсальнаяДата() - Дата(1970,1,1,0,0,0);
	ТокенДоступа.ВремяЖизни                    = 60;
	ТокенДоступа.Идентификатор                 = Новый УникальныйИдентификатор;
	
	ТокенДоступа.Подписать(АлгоритмПодписиТокенаДоступа.HS256, КлючПодписи);
	
	Возврат Строка(ТокенДоступа);
	
КонецФункции

Функция КлючПодписиТокенаДоступа()
	
	Владелец = ОбщегоНазначения.ИдентификаторОбъектаМетаданных("Константа.ЛМС_ИспользоватьТокеныДоступаДляАвторизацииСВнешнимиРесурсами");
	Возврат ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища(Владелец, "КлючПодписи");
	
КонецФункции

Функция HTTPОтвет(ОтправляемыеДанные, КодСостояния = 200)
	
	Ответ = Новый HTTPСервисОтвет(КодСостояния);
	Ответ.Заголовки.Вставить("Accept-Charset", "utf-8");
	Ответ.Заголовки.Вставить("Content-Type", "application/json;charset=utf-8");
	Ответ.Заголовки["Cache-Control"] = "no-cache";
	
	Ответ.УстановитьТелоИзСтроки(ЗаписатьДанныеJSON(ОтправляемыеДанные));
	
	Возврат Ответ;
	
КонецФункции

Функция ЗаписатьДанныеJSON(ДанныеДляЗаписи)
	
	ДанныеJSON = "";
	Если ДанныеДляЗаписи = Неопределено Тогда
		Возврат ДанныеJSON;
	КонецЕсли;
	
	Запись = Новый ЗаписьJSON;
	Запись.УстановитьСтроку(Новый ПараметрыЗаписиJSON);
	
	ЗаписатьJSON(Запись, ДанныеДляЗаписи);
	ДанныеJSON = Запись.Закрыть();
	
	Возврат ДанныеJSON;
	
КонецФункции

Функция ПрочитатьДанныеJSON(ДанныеJSON, ИменаСвойствСоЗначениямиДата = "")
	
	ЧтениеJSON = Новый ЧтениеJSON;
	ЧтениеJSON.УстановитьСтроку(ДанныеJSON);
	
	Возврат ПрочитатьJSON(ЧтениеJSON, , ИменаСвойствСоЗначениямиДата);
	
КонецФункции

#КонецОбласти // СлужебныеПроцедурыИФункции

 


Представление кода по созданию токена.

Функция ТокенДоступаКВебСервису(ИмяПолучателяТокена, КлючПодписи)
	
	ТокенДоступа = Новый ТокенДоступа;
	ТокенДоступа.Заголовки.Вставить("alg", "HS256");
	
	ТокенДоступа.Эмитент                       = "ssl";
	ТокенДоступа.Получатели                    = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(ИмяПолучателяТокена);
	ТокенДоступа.КлючСопоставленияПользователя = "Web_Service";
	ТокенДоступа.ВремяСоздания                 = ТекущаяУниверсальнаяДата() - Дата(1970,1,1,0,0,0);
	ТокенДоступа.ВремяЖизни                    = 60;
	ТокенДоступа.Идентификатор                 = Новый УникальныйИдентификатор;
	
	ТокенДоступа.Подписать(АлгоритмПодписиТокенаДоступа.HS256, КлючПодписи);
	
	Возврат Строка(ТокенДоступа);
	
КонецФункции

 

Для формирования токена доступа в систему мы воспользуемся новым объектом «ТокенДоступа». Представим описание свойств данного объекта:

 

Свойства объекта ТокенДоступа

Свойства HTTP-сервиса в файле default.vrd

Описание свойства

Эмитент (соответствует ключу «iss»)

authenticationClaimName

Указывается как «ssl». Идентификатор приложения, выдавшего токен.

Получатели (соответствует ключу «aud»)

accessTokenRecepientName

Указывается идентификатор или любое другое значение в виде строки идентифицируя получателя токена.

КлючСопоставленияПользователя (соответствует ключу «sub»)

authenticationUserPropertyName

Указывается имя пользователя, под которым происходит аутентификация.

 

ВремяСоздания рассчитывается как ТекущаяУниверсальнаяДата() - Дата(1970,1,1,0,0,0). Это числовое значение времени создания токена доступа в формате UnixTime (количество секунд, прошедших с полуночи 01.01.1970).  Соответствует ключам «iat» и «nbf».

ВремяЖизни указывается в секундах. Можно выбрать любую продолжительность жизни токена. Устанавливает значение для ключа «exp» равное сумме времени создания и времени жизни.

КлючПодписи мы используем непосредственно в методе «ТокенДоступа.Подписать», но он также хранится в свойствах файла с именем «keyInformation».

Идентификатор является уникальным идентификатором токена. Соответствует ключу «jti».

Для получения токена мы выполняем обращение к нашему сервису авторизации.
Выполняем запрос с методом POST на адрес «localhost/edo/hs/auth/service/» с идентификационными данными в виде логина и пароля (AccessTokenGenerationUser, K3QvQz2y). Запрос должен включать в себя тело

 

 

Свойство AccessTokenRecepientName содержит идентификатор сервиса (к нему будет относиться будущий токен), с которым мы можем обращаться только к сервисам, где в файле default.vrd будет указано это же значение в свойстве AccessTokenRecepientName. В случае успеха получим такой ответ:

 

 

Ответ будет состоять из текста ошибки если что-то произошло не так и не удалось сформировать токен и самого токена в виде закодированной строки в формате Base64. Таким образом нам удалось сформировать наш токен и дальше мы можем с ним обратиться к своему HTTP-сервису. 

Содержимое полученного токена можно посмотреть с помощью сайта JWT.IO (справочная информация п. 3). 


 

Чтобы сервер аутентификации понял наш запрос с токеном, необходимо скорректировать данные сервиса в файле публикации. Заходим в файл default.vrd и находим там наш сервис (если он уже был опубликован ранее) или добавляем новый.

<service name="Клиенты"
        rootUrl="Clients"
        enable="true"
        reuseSessions="autouse"
        sessionMaxAge="20"
        poolSize="10"
        poolTimeout="5">
        <accessTokenAuthentication>
			<issuers>
				<issuer name="ssl" 
					authenticationClaimName="sub" 
					authenticationUserPropertyName="PublishingDatabaseService" 
					keyInformation="0J/RgNC40LLQtdGCINC80LjRgCE="/>
            </issuers>
			<accessTokenRecepientName>4e044f77-2563-4f19-bcee-fead012e2584</accessTokenRecepientName>
        </accessTokenAuthentication>
</service>

 


Добавляем в свойства пользователя PublishingDatabaseService информацию о том, что он может быть аутентифицирован с помощью токена доступа.

 


Отлично, теперь наша публикация откорректирована и мы можем обратиться к нашему сервису clients c целью получения информации о каком-нибудь клиенте.

ПараметрыЗапроса = Новый Структура;
ПараметрыЗапроса.Вставить("clientId", XMLСтрока(КлиентСсылка));

ТелоЗапроса = ЗаписатьДанныеJSON(ПараметрыЗапроса);

ШаблонСтроки = "http://%1/hs/clients/information";
СтрокаURI    = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонСтроки, "localhost/edo");
СтруктураURI = ОбщегоНазначенияКлиентСервер.СтруктураURI(СтрокаURI);

HTTPЗапрос = Новый HTTPЗапрос(СтруктураURI.ПутьНаСервере);            
HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапроса);

УстановитьПривилегированныйРежим(Истина);

ИспользоватьТокеныДоступа = ПолучитьФункциональнуюОпцию("ИспользоватьТокеныДоступаДляАвторизацииСВнешнимиРесурсами");
Если ИспользоватьТокеныДоступа Тогда
	HTTPЗапрос.Заголовки.Вставить("Authorization", "Bearer " + ТокенДоступаКВебСервису(ИдентификаторВебСервисаИнформацииОКлиентах()));
КонецЕсли;

РезультатВыполнения = ВыполнитьЗапрос(HTTPЗапрос, СтруктураURI, "GET");

Представление кода метода ВыполнитьЗапроса

 
 
Функция ВыполнитьЗапрос(HTTPЗапрос, СтруктураАдреса, HTTPМетод = "")
	
	Попытка
		
		HTTPСоединение = НовоеHTTPСоединение(СтруктураАдреса);
		
		Если ПустаяСтрока(HTTPМетод) Тогда
			HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
		Иначе
			HTTPОтвет = HTTPСоединение.ВызватьHTTPМетод(HTTPМетод, HTTPЗапрос);
		КонецЕсли;
		
	Исключение
		
		ЗаписатьОшибкуВЖурналРегистрации(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		     НСтр("ru = 'Не удалось установить соединение с сервером %1 по причине:
		     |%2'"), СтруктураАдреса.Хост, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())));
		
		ВызватьИсключение; 
		
	КонецПопытки;
	
	Результат = Новый Структура;
	Результат.Вставить("ЗапросВыполнен", Ложь);
	Результат.Вставить("ОтветСервера", "");
	
	Если HTTPОтвет.КодСостояния = 401
		Или HTTPОтвет.КодСостояния = 403 Тогда
		
		ТекстОшибки = НСтр("ru = 'Не удалось пройти авторизацию на сервере.'"); 
		ЗаписатьОшибкуВЖурналРегистрации(ТекстОшибки);
		
		ВызватьИсключение ТекстОшибки;
		
	КонецЕсли;
	
	Результат.ЗапросВыполнен = HTTPОтвет.КодСостояния = 200;
	Результат.ОтветСервера = HTTPОтвет.ПолучитьТелоКакСтроку();
	
	Если Не Результат.ЗапросВыполнен Тогда
		
		ЗаписатьОшибкуВЖурналРегистрации(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		     НСтр("ru = 'Не удалось установить соединение с сервером %1 по причине:
		     |%2'"), СтруктураАдреса.Хост, Результат.ОтветСервера));
		
		ВызватьИсключение Результат.ОтветСервера;
		
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

 

 

 
 
Функция НовоеHTTPСоединение(СтруктураURI, Таймаут = 60) Экспорт
	
	ИнтернетПрокси = Неопределено;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ПолучениеФайловИзИнтернета") Тогда
		МодульПолучениеФайловИзИнтернета = ОбщегоНазначения.ОбщийМодуль("ПолучениеФайловИзИнтернета");
		ИнтернетПрокси = МодульПолучениеФайловИзИнтернета.ПолучитьПрокси(СтруктураURI.Схема);
	КонецЕсли;
	
	ЗащищенноеСоединение = Неопределено;
	
	Если ВРег(СтруктураURI.Схема) = "HTTPS" Или ВРег(СтруктураURI.Схема) = "FTPS" Тогда
		ЗащищенноеСоединение = ОбщегоНазначенияКлиентСервер.НовоеЗащищенноеСоединение();
	КонецЕсли;
	
	Соединение = Новый HTTPСоединение(СтруктураURI.Хост, СтруктураURI.Порт, СтруктураURI.Логин, СтруктураURI.Пароль, ИнтернетПрокси, Таймаут, ЗащищенноеСоединение);
	
	Возврат Соединение;
	
КонецФункции

 

 

Справочные материалы

  1. Что такое токен и как он устроен https://its.1c.ru/db/v8321doc#bookmark:dev:TI000002522
  2. Описание полей элемента «accessTokenAuthentication» в файле публикации default.vrd https://its.1c.ru/db/v8321doc#bookmark:adm:TI000001102;
  3. Для экспериментов с различным наполнением JWT можно использовать сайт https://jwt.io.

Аутентификация1С

См. также

Информационная безопасность Системный администратор Программист Платные (руб)

AUTO VPN (portable) - автоматизация подключения пользователей к удаленному рабочему месту или сети посредством создания автоматического VPN (L2TP или L2TP/IPSEC и т.д.) подключения без ввода настроек пользователем (с возможностью скрытия этих настроек от пользователя). Программа автоматически выполняет подключение к VPN серверу и после успешного коннекта , если необходимо, подключение к серверу удаленных рабочих столов (RDP).

1200 руб.

24.03.2020    14824    24    32    

34

Информационная безопасность Пароли Платформа 1С v8.3 Бесплатно (free)

Все еще храните пароли в базе? Тогда мы идем к вам! Безопасное и надежное хранение секретов. JWT авторизация. Удобный интерфейс. Демо конфигурация. Бесплатно.

30.05.2024    4686    kamisov    16    

59

Информационная безопасность Программист Платформа 1С v8.3 Абонемент ($m)

Интеграционные решения стали неотъемлемой частью нашей жизни. Правилом хорошего тона в современных приложениях является не давать интегратору доступ к чувствительным данным. Device flow позволяет аутентифицировать пользователя, не показывая приложению чувствительные данные (например: логин и пароль)<br> Рассмотрим Device flow аутентификацию, в приложении, на примере OpenID провайдера Yandex.

1 стартмани

27.10.2023    2123    platonov.e    1    

23

Информационная безопасность Системный администратор Платформа 1С v8.3 Конфигурации 1cv8 Россия Абонемент ($m)

Продукты на основе решений 1С уверенной поступью захватывают рынок учётных систем в стране. Широкое распространение программ всегда порождает большой интерес к ним со стороны злоумышленников, а пользователь 1С это одна из дверей в защищённый информационный контур предприятия. Обработка позволяет быстро и комплексно оценить настройки безопасности конкретной базы и возможности пользователя этой базы на сервере. Также можно оценить некоторые аспекты сетевой безопасности предприятия со стороны сервера 1С.

5 стартмани

24.04.2023    5894    18    soulner    7    

32

Информационная безопасность Системный администратор Программист Платформа 1С v8.3 1С:ERP Управление предприятием 2 Абонемент ($m)

1С, начиная с версии платформы 8.3.21, добавили в систему возможность двойной аутентификации. Как это работает: в пользователе информационной базы появилось свойство «Аутентификация токеном доступа» (АутентификацияТокеномДоступа во встроенном языке), если установить этот признак и осуществить ряд манипуляций на встроенном языке, то появляется возможность при аутентификации отправлять HTTP запросы, которые и реализуют этот самый второй фактор. Данное расширение позволяет организовать двухфакторную аутентификацию с помощью электронной почты или мессенджера Telegram.

2 стартмани

08.12.2022    6611    40    Silenser    12    

23

Информационная безопасность Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

От клиента клиенту, от одной системы к другой, мы вновь и вновь встречаем одни и те же проблемы и дыры в безопасности. На конференции Infostart Event 2021 Post-Apocalypse Виталий Онянов рассказал о базовых принципах безопасности информационных систем и представил чек-лист, с помощью которого вы сможете проверить свою систему на уязвимость.

26.10.2022    10062    Tavalik    46    

114
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. fomix 33 28.02.24 07:53 Сейчас в теме
Про http сервис более, чем понятно. А вот тема зачем всё это нужно вообще не раскрыта.
dabu-dabu; Sergik_D; SP2000; PROSTO-1C; ixijixi; +5 Ответить
2. PROSTO-1C 1277 28.02.24 11:47 Сейчас в теме
(1) Спасибо, мы рады, что получилось понятно написать инструкцию. Мы не хотели повторяться и дублировать информацию — «зачем все это нужно» можно посмотреть в документации 1С https://its.1c.ru/db/v8321doc#bookmark:dev:TI000002523
3. kamisov 214 29.02.24 11:03 Сейчас в теме
Жаль только, что 1С формирует неправильные JWT. И понимает созданные в 1С JWT только сама 1С. Потому что поля nbf и iat должны быть числами, а не строками. То что они строки - видно на вашем же скриншоте с JWT.io

Если навести курсор на значения nbf и iat на сайте - он покажет «invalid что-то там».

Ждём когда 1С починит формирование JWT.
kaaasteeen; JohnyDeath; bettereatnuts; PROSTO-1C; +4 Ответить
4. amoarok 99 29.02.24 21:59 Сейчас в теме
(3) можно ждать, а можно формировать токены вручную. Код совсем не сложный получается.
Главное что http-сервисы платформы умеют корректно обрабатывать как "неправильные" JWT, так и правильные.
PROSTO-1C; +1 Ответить
5. kamisov 214 29.02.24 23:12 Сейчас в теме
(4) есть пример совсем несложного кода для линукса? Только для винды видел с использованием ком-объектов из дотнета.
6. amoarok 99 01.03.24 00:07 Сейчас в теме
(5) для симметричного шифрования, как в статье, есть готовая библиотека: https://github.com/pintov/1c-jwt
Для RSA можно например воспользоваться OpenSSL для подписания. Но и на чистом 1Сном коде реально написать - у нас в проде такое крутится, но там уже сложновато. Я честно говоря был удивлён когда понял что 1С в качестве числодробилки может оперировать с ОЧЕНЬ большими числами.
PROSTO-1C; +1 Ответить
7. kamisov 214 01.03.24 00:30 Сейчас в теме
(6) в общем, костыли :) А хотелось от 1С, конечно, увидеть работающий токен доступа.
8. user612295_death4321 03.03.24 17:31 Сейчас в теме
(3)
Ждём когда 1С починит формирование JWT.


Ты ведь оформил тикет в 1С ?)
9. kamisov 214 03.03.24 17:51 Сейчас в теме
(8) да, и ждал 3 месяца пока зарегистрируют. Теперь буду ждать 100 лет когда починят.
10. AntonKite 23.04.24 08:24 Сейчас в теме
Уже была аналогичная статья, в которой визуально показали связь между созданием токена доступа и файлом публикации.
+ есть описание как создавать и применять полученные токены доступа для авторизации в веб и тонком клиентах.
https://forum.infostart.ru/forum15/topic294665/#message3042387
Оставьте свое сообщение