При разработке собственных API на платформе 1С:Предприятие часто возникает задача аутентификации пользователей и обеспечения безопасности взаимодействия. Одним из современных и широко используемых подходов является применение JSON Web Token (JWT) в качестве "Bearer token". Однако, для разработчиков, работающих с версией платформы 1С 8.3.18, создание JWT-токенов стандартными средствами представляет определенные сложности, поскольку встроенная поддержка этого функционала появилась лишь в версии 8.3.21.
В этой статье мы подробно рассмотрим, как можно реализовать генерацию JWT-токенов на платформе 1С:Предприятие 8.3.18, обходя ограничения стандартных средств, а также обсудим ключевые аспекты работы с этим типом токенов.
Когда мы говорим о создании "большого токена" или, как уточнил автор, JWT-токена в 1С 8.3.18, мы сталкиваемся с тем, что в этой версии платформы отсутствуют специализированные объекты, такие как ТокенДоступа, которые значительно упрощают работу с JWT в более новых релизах (начиная с 8.3.21). Это означает, что для версий 8.3.18 и более ранних, нам придется либо реализовать весь процесс генерации и подписи JWT вручную, либо использовать сторонние компоненты и библиотеки.
Давайте выясним, что представляет собой JWT, прежде чем углубиться в детали его реализации.
Прежде чем перейти к технической реализации, давайте разберемся, что такое JWT и почему он так важен для современной аутентификации. JWT – это компактный, URL-безопасный способ представления информации между сторонами в виде JSON-объекта. Его основное преимущество заключается в том, что он самодостаточен: вся необходимая информация для проверки токена содержится внутри него самого, что позволяет серверу-получателю проверять его без необходимости каждый раз обращаться к серверу авторизации.
Мы рассмотрим структуру JWT, которая состоит из трех частей, разделенных точками:
Каждая из этих частей представляет собой JSON-объект, закодированный в формате Base64Url.
В заголовке мы указываем информацию о типе токена и используемом алгоритме шифрования. Обычно тип токена (typ) всегда "JWT", а алгоритм (alg) может быть, например, "HS256" для HMAC-SHA256, "RS256" для RSA-SHA256 и так далее. Мы будем использовать "HS256" как один из наиболее распространенных вариантов.
Пример JSON-структуры заголовка:
{
"alg": "HS256",
"typ": "JWT"
}
Полезная нагрузка содержит так называемые "заявки" (claims) — утверждения о сущности (обычно о пользователе) и дополнительные данные. Эти данные могут включать идентификатор пользователя, его роли, время выдачи токена (iat), время истечения срока действия (exp) и любую другую информацию, необходимую для авторизации. Важно помнить, что данные в Payload не шифруются, а только кодируются, поэтому не следует помещать туда конфиденциальную информацию.
Пример JSON-структуры полезной нагрузки:
{
"sub": "1234567890",
"name": "Иванов Иван",
"admin": true,
"iat": 1516239022,
"exp": 1516242622
}
Здесь:
sub (subject) – идентификатор пользователя.name – имя пользователя.admin – флаг администратора.iat (issued at) – время выдачи токена (в формате Unix timestamp).exp (expiration time) – время истечения срока действия токена (в формате Unix timestamp).Подпись – это критически важная часть JWT, которая используется для проверки целостности токена и подтверждения того, что он не был подделан. Она вычисляется путем применения алгоритма хеширования (указанного в заголовке) к закодированным заголовку и полезной нагрузке, а также к секретному ключу. Секретный ключ известен только серверу-эмитенту и серверу-валидатору, что позволяет им удостовериться в подлинности токена.
Процесс вычисления подписи выглядит следующим образом:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
Итоговый JWT-токен будет выглядеть как строка из трех частей, разделенных точками:
base64UrlEncode(header) + "." + base64UrlEncode(payload) + "." + base64UrlEncode(signature)
Поскольку в версии 8.3.18 у нас нет готового объекта ТокенДоступа, мы будем реализовывать генерацию JWT вручную, используя доступные средства платформы. Это потребует нескольких шагов, включая формирование JSON, кодирование в Base64Url и генерацию криптографической подписи.
На первом шаге мы формируем строковые представления JSON-объектов для заголовка и полезной нагрузки. Для этого мы можем использовать стандартные объекты 1С для работы с JSON, такие как ЗаписьJSON и ЧтениеJSON, а также базовые структуры данных Структура и Массив.
Рассмотрим пример создания заголовка:
Функция СформироватьЗаголовокJWT()
Заголовок = Новый Структура();
Заголовок.Вставить("alg", "HS256"); // Алгоритм шифрования
Заголовок.Вставить("typ", "JWT"); // Тип токена
СтрокаJSONЗаголовка = "";
ЗаписьJSON = Новый ЗаписьJSON();
ЗаписьJSON.УстановитьСтроку();
ЗаписьJSON.Записать(Заголовок);
СтрокаJSONЗаголовка = ЗаписьJSON.Закрыть();
Возврат СтрокаJSONЗаголовка;
КонецФункции
Аналогичным образом мы формируем полезную нагрузку. Важно включать в нее не только идентификационные данные, но и, например, время выдачи и время истечения токена для обеспечения безопасности и управления жизненным циклом.
Рассмотрим пример создания полезной нагрузки:
Функция СформироватьПолезнуюНагрузкуJWT(ИдентификаторПользователя, ИмяПользователя, ПраваАдминистратора)
ПолезнаяНагрузка = Новый Структура();
ПолезнаяНагрузка.Вставить("sub", ИдентификаторПользователя);
ПолезнаяНагрузка.Вставить("name", ИмяПользователя);
ПолезнаяНагрузка.Вставить("admin", ПраваАдминистратора);
// Время выдачи токена (iat - issued at)
ТекущееВремя = ТекущаяДата();
ПолезнаяНагрузка.Вставить("iat", Цел(ТекущееВремя / 1000)); // Unix timestamp в секундах
// Время истечения срока действия токена (exp - expiration time), например, 1 час
ВремяИстечения = ТекущееВремя + 3600; // Через 1 час
ПолезнаяНагрузка.Вставить("exp", Цел(ВремяИстечения / 1000)); // Unix timestamp в секундах
СтрокаJSONПолезнойНагрузки = "";
ЗаписьJSON = Новый ЗаписьJSON();
ЗаписьJSON.УстановитьСтроку();
ЗаписьJSON.Записать(ПолезнаяНагрузка);
СтрокаJSONПолезнойНагрузки = ЗаписьJSON.Закрыть();
Возврат СтрокаJSONПолезнойНагрузки;
КонецФункции
После того как мы получили строковые представления JSON для заголовка и полезной нагрузки, их необходимо закодировать в формат Base64Url. Этот формат является вариантом Base64, специально адаптированным для использования в URL-адресах, где некоторые символы (например, '+', '/', '=') имеют особое значение и должны быть заменены.
Мы используем стандартную функцию 1С КодироватьДвоичныеДанныеВБазе64, а затем вручную заменим символы для соответствия стандарту Base64Url.
Рассмотрим функцию для кодирования в Base64Url:
Функция КодироватьВBase64Url(ИсходнаяСтрока)
// Преобразуем строку в двоичные данные
ДвоичныеДанные = Новый ДвоичныеДанные(ИсходнаяСтрока, КодировкаТекста.UTF8);
// Кодируем в Base64
СтрокаBase64 = КодироватьДвоичныеДанныеВБазе64(ДвоичныеДанные);
// Заменяем символы для Base64Url
СтрокаBase64Url = СтрЗаменить(СтрокаBase64, "+", "-");
СтрокаBase64Url = СтрЗаменить(СтрокаBase64Url, "/", "_");
// Удаляем символы '=' в конце, если они есть
Пока Прав(СтрокаBase64Url, 1) = "=" Цикл
СтрокаBase64Url = Лев(СтрокаBase64Url, СтрДлина(СтрокаBase64Url) - 1);
КонецЦикла;
Возврат СтрокаBase64Url;
КонецФункции
Теперь мы можем применить эту функцию к нашим JSON-строкам:
ЗакодированныйЗаголовок = КодироватьВBase64Url(СформироватьЗаголовокJWT());
ЗакодированнаяПолезнаяНагрузка = КодироватьВBase64Url(СформироватьПолезнуюНагрузкуJWT(ИдентификаторПользователя, ИмяПользователя, ПраваАдминистратора));
Следующий шаг – объединить закодированные заголовок и полезную нагрузку в одну строку, которая будет использоваться для генерации подписи. Эти две части соединяются точкой.
СтрокаДляПодписи = ЗакодированныйЗаголовок + "." + ЗакодированнаяПолезнаяНагрузка;
Генерация подписи – это наиболее технически сложный этап для версии 8.3.18, поскольку стандартные средства платформы напрямую не предоставляют удобных функций для реализации алгоритмов хеширования с ключом, таких как HMAC-SHA256. Нам потребуется применить выбранный алгоритм хеширования к СтрокаДляПодписи, используя секретный ключ.
Существует несколько путей решения этой проблемы:
CAPICOM.HashedData или System.Security.Cryptography.HMACSHA256 из .NET, если есть возможность использовать COM-объекты .NET). Однако, это может быть связано с дополнительными настройками на сервере и не всегда является "стандартным" решением 1С в чистом виде.pintov/1c-jwt (доступная на GitHub) специально создана для реализации HMAC и JSON Web Token в 1С:Предприятие, начиная с версии 8.3.10. Также упоминается библиотека crypto для OScript, которая включает класс JWT с поддержкой HMAC и алгоритма HS256. Эти решения значительно упрощают задачу, предоставляя готовые функции для криптографических операций.Предположим, что мы нашли или реализовали функцию ВычислитьHMACSHA256(Строка, СекретныйКлюч). Тогда код будет выглядеть так:
// Секретный ключ должен быть надежно хранимым и уникальным для вашего приложения.
// Никогда не делайте его частью клиентского кода или общедоступным.
СекретныйКлюч = "ВашОченьСекретныйКлючКоторыйНеобходимоНадежноХранить";
// Здесь мы имитируем вызов функции, которая выполняет HMAC-SHA256
// В реальной реализации здесь будет вызов внешней компоненты или COM-объекта
ДвоичнаяПодпись = ВашаФункцияВычисленияHMACSHA256(СтрокаДляПодписи, СекретныйКлюч);
В случае использования внешней компоненты, например, pintov/1c-jwt, вы могли бы иметь что-то вроде:
// Инициализация внешней компоненты
// Компонента = Новый ("AddIn.НазваниеКомпоненты");
// ...
// ДвоичнаяПодпись = Компонента.HMACSHA256(СтрокаДляПодписи, СекретныйКлюч);
Полученную двоичную подпись (или строку, если ваша функция возвращает строку) также необходимо закодировать в формат Base64Url. Мы используем ту же функцию КодироватьВBase64Url, которую разработали ранее.
ЗакодированнаяПодпись = КодироватьВBase64Url(ДвоичнаяПодпись);
Если функция ВашаФункцияВычисленияHMACSHA256 возвращает строку, то, возможно, потребуется сначала преобразовать ее в двоичные данные перед кодированием, или же функция КодироватьВBase64Url должна быть адаптирована для работы напрямую с двоичными данными, если это не так.
Важно убедиться, что криптографическая функция возвращает именно двоичные данные хеша, поскольку кодировать в Base64Url нужно именно их, а не их строковое представление.
Наконец, мы объединяем все три закодированные части (заголовок, полезную нагрузку и подпись) точками, чтобы получить финальный JWT-токен.
ИтоговыйJWTТокен = ЗакодированныйЗаголовок + "." + ЗакодированнаяПолезнаяНагрузка + "." + ЗакодированнаяПодпись;
Теперь у нас есть готовый JWT-токен, который может быть выдан клиенту (например, через HTTP-сервис 1С) и использоваться им для аутентификации в вашем API. Для отладки и проверки таких HTTP-сервисов можно использовать инструмент для тестирования REST API в среде 1С.
Как мы уже упоминали, ручная реализация криптографических алгоритмов может быть сложной и рискованной. Поэтому для 1С:Предприятие 8.3.18 мы настоятельно рекомендуем рассмотреть использование уже готовых внешних компонентов или библиотек, разработанных сообществом.
Эти библиотеки уже содержат проверенные реализации необходимых криптографических функций, таких как HMAC-SHA256, и упрощают процесс генерации и проверки JWT. Они позволяют нам не изобретать велосипед и сосредоточиться на бизнес-логике нашего API.
Преимущества использования внешних компонентов:
Изучите доступные решения на платформах для разработчиков 1С, таких как GitHub или Инфостарт, чтобы найти подходящую библиотеку. При выборе обращайте внимание на версию платформы, с которой совместима библиотека, и на активность ее поддержки. В качестве готового решения подойдёт обработка для создания и проверки токенов JWT с подписью HS256.
Реализовав генерацию JWT, мы получаем мощный инструмент для аутентификации. Однако, его эффективное и безопасное использование требует понимания как преимуществ, так и сопутствующих аспектов безопасности.
Несмотря на отсутствие прямой поддержки JSON Web Token в 1С:Предприятие 8.3.18, мы выяснили, что его можно успешно реализовать. Для этого нам необходимо поэтапно сформировать заголовок, полезную нагрузку, закодировать их в Base64Url, а затем сгенерировать подпись с использованием HMAC-SHA256 и секретного ключа. Самой сложной частью является криптографическая подпись, для которой мы настоятельно рекомендуем использовать проверенные внешние компоненты, а не пытаться реализовать ее самостоятельно на встроенном языке 1С.
Внедрение JWT в вашу систему API на 1С позволит нам создать гибкую, масштабируемую и безопасную систему аутентификации. Всегда помните о важности надежного хранения секретного ключа и использовании HTTPS для обеспечения максимальной безопасности ваших приложений.