Многофакторная авторизация. Шаблон проектирования для «1С:Предприятие 8». На примере API «Приватбанка» для юр. лиц

11.09.17

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

Многим из читателей знакомы понятия стандартов разработки и шаблонов проектирования. Для платформы «1С:Предприятие 8» на сайте its.1c.ru описаны базовые стандарты оформления кода и некоторые полезные примеры, но отсутствует информация об высокоуровневых абстракциях. Почти у каждого банка есть реализация обмена с конфигурациями «1С:Предприятие 8», но анализировать код, а тем более реализацию без слёз невозможно. Данная статья предлагает использовать некий шаблон оформления кода для многофакторной авторизации.

Прежде чем начать

Как тестовый пример будем рассматривать описание API банка «Приватбанк». Детально описание можно найти по ссылке. Данный пример хорош тем, что затрагивает взаимодействие с пользователем, мобильными номерами (OTP-авторизация, правда не во всех случаях), а так же имеется разграничения прав как на уровне приложения так и на уровне доступной роли клиента. Даже беглое описание, наверное, вызывает определенные сложности в оценке времени на разработку, а так же вопросы по поводу организации кода.

Анализируем шаги авторизации

Весь процесс состоит из четырех последовательных шагов:

  1. Получение ID сессии — состоит в том, что бы по R03;ID и R03;secret приложения получить R03;R03;token, который будет передаваться с каждым запросом к Web-сервису банка, в данном случае так же получим права доступа;
  2. Авторизация с помощью пары логин/пароль пользователя — основная цель это повысить права доступа R03;token полученного на предыдущем шаге;
  3. Прохождение OTP-авторизации — если используется двух-факторная авторизация необходимо отправить запрос на получения OTP-пароля, который придет на мобильное устройство;
  4. Проверка OTP-авторизации — отправляем подтверждение повышения прав с помощью OTP-пароля.

Определим объекты которые будут необходимы:

  • Id — идентификатор сессии;
  • ExpiresIn — дата в формате Unix Timestamp, когда истечет сессия;
  • ApplicationId — идентификатор приложения;
  • ApplicationSecret — секрет приложения;
  • Login — логин пользователя;
  • Password — пароль пользователя;
  • OtpDev — устройство для получения OTP-пароля;
  • Otp OTP-пароль полученный на устройство;
  • Роли — таблица ролей доступных сессии.

Срок истечения сессии достаточно мал, получается, что нет смысла выполнять предварительную авторизацию и хранить данные сессии. Логично сохранять только текущую сессию и при каждом запросе к Web-сервису банка проверять валидность сессии и необходимые права доступа к команде сервиса. При надобности обновлять сессию и повышать права к определенному уровню. Теперь можно переходить к псевдокоду.

ШАГ 0. Выбор команды Web-сервиса банка

После предварительного анализа уже понятно, что необходимо для каждого запроса выполнять проверку сессии и если есть потребность проходить авторизацию. Сделаем это с помощью псевдокода для выписок банка:

&НаКлиенте
Процедура ВыпискиПоСчетуНаКлиенте()
    
    ВыпискиИзБанка.Очистить();
    
    ОписаниеОповещения = Новый ОписаниеОповещения(
        "ВыполнитьОбновлениеВыписокПоСчету", 
        ЭтотОбъект, 
        Новый Структура("Роль", "ROLE_P24_BUSINESS"));
    
    АвторизироватьКлиентБанк(ОписаниеОповещения);
        
КонецПроцедуры // ВыпискиПоСчетуНаКлиенте()

&НаКлиенте
Процедура ВыполнитьОбновлениеВыписокПоСчету(Результат, 
    ДополнительныеПараметры = Неопределено) Экспорт
    
    Если Результат = Истина Тогда
        ОбновитьДанныеПоСчетуНаСервере();
    КонецЕсли;
    
КонецПроцедуры // ВыполнитьОбновлениеВыписокПоСчету()

Происходит очистка объекта в который будет выполнятся загрузка выписок банка, далее создается описание оповещения, которое будет выполнено при актуальной сессии и доступной роли для данной операции (Роль передается как структура в дополнительных параметрах).

ШАГ 1. Получаем ID сессии

&НаКлиенте
Процедура АвторизироватьКлиентБанк(ОписаниеЭтапаПолученияДанных) 

    // Очищаем чтобы проверить доступные роли для токена и сравнить с необходимой 
    // ролью для выполнения запроса указаного в описании оповещения.
    Роли.Очистить();
    
    // Проходим авторизацию приложения
    ПриложениеАвторизация = АвторизироватьПриложение();
    ОписаниеСледующиегоЭтапаАвторизации = Новый ОписаниеОповещения(
        "ВыполнитьПослеАвторизацииПриложения", 
        ЭтотОбъект, 
        ПриложениеАвторизация);
    
    ОбработатьРезультатАвторизации(ПриложениеАвторизация, 
        ОписаниеЭтапаПолученияДанных, 
        ОписаниеСледующиегоЭтапаАвторизации); 
     
КонецПроцедуры // АвторизироватьКлиентБанк()

&НаСервере
Функция АвторизироватьПриложение()
    
    // Заполняем ApplicationId, ApplicationSecret, Login, Password из БД или данных формы.
    ЗаполнитьДанныеАвторизации(); 
    
    // Id сессии может быть заполнен, тогда при вызове необходимо проверить его валидность
    // https://link.privatbank.ua/console/wiki/client_auth Валидация SessionID.
    ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
    РезультатАвторизации = ОбработкаОбъект.АвторизироватьПриложение(
        ApplicationId, ApplicationSecret, Id);
    ЗначениеВРеквизитФормы(ОбработкаОбъект, "Объект");
    
    Возврат РезультатАвторизации;  
  
КонецФункции // АвторизироватьПриложение()

После авторизации или валидации приложения, необходимо проверить результат выполнения. Если валидация сессии прошла успешно и роль для вызова операции Web-сервера банка доступна, тогда выполняем запрос на получения полезных данных, иначе переходим на следующий этап авторизации.

&НаКлиенте
Процедура ОбработатьРезультатАвторизации(Результат, 
    ОписаниеЭтапаПолученияДанных, 
    ОписаниеСледующиегоЭтапаАвторизаци = Неопределено)
    
    Перем МассивРолей;
    
    Если ТипЗнч(Результат) = Тип("ФиксированнаяСтруктура") Тогда
        
        Результат.Свойство("Id", Id); 
        Результат.Свойство("ExpiresIn", ExpiresIn);
        Если Результат.Свойство("Roles", МассивРолей) Тогда
            
            Роли.Очистить();
            Для Каждого ЭлементМассива Из МассивРолей Цикл
                Роли.Добавить().Роль = ЭлементМассива;   
            КонецЦикла;
            
        КонецЕсли;
          
    КонецЕсли;
    
    АвторизацияПройдена = Ложь;
    Если ТипЗнч(ОписаниеЭтапаПолученияДанных) = Тип("ОписаниеОповещения") Тогда
        
        ПараметрыОтбора = ОписаниеЭтапаПолученияДанных.ДополнительныеПараметры;
        Если ТипЗнч(ПараметрыОтбора) = Тип("Структура") Тогда 
            Если Роли.НайтиСтроки(ПараметрыОтбора).Количество() > 0 Тогда;
                АвторизацияПройдена = Истина;
                ВыполнитьОбработкуОповещения(ОписаниеЭтапаПолученияДанных, Истина);        
            КонецЕсли;        
        КонецЕсли;
        
    КонецЕсли;
    
    Если АвторизацияПройдена = Ложь Тогда
        Если ТипЗнч(ОписаниеСледующиегоЭтапаАвторизаци) = Тип("ОписаниеОповещения") Тогда
            ВыполнитьОбработкуОповещения(ОписаниеСледующиегоЭтапаАвторизаци, 
                ОписаниеЭтапаПолученияДанных);    
        КонецЕсли;
    КонецЕсли;
      
КонецПроцедуры // ОбработатьРезультатАвторизации()

ШАГ 2. Авторизация с помощью пары логин/пароль

// Процедура выполняется после успешной авторизации приложения и если доступные
// роли не удовлетворяют требованиям выполняемого запроса к клиент-банку.
// 
// Параметры:
//  ОписаниеЭтапаПолученияДанных - ОписаниеОповещения - оповещение которое будет выполнено если 
//                                                      авторизация будет успешной.
//  ПриложениеАвторизация        - Произвольный       - результат предыдущего этапа авторизации.
//              
&НаКлиенте
Процедура ВыполнитьПослеАвторизацииПриложения(ОписаниеЭтапаПолученияДанных,
    ПриложениеАвторизация = Неопределено) Экспорт
    
    // Проходим авторизацию клиента
    КлиентАвторизация = АвторизироватьКлиента();
    ОписаниеСледующиегоЭтапаАвторизации = Новый ОписаниеОповещения(
        "ВыполнитьПослеАвторизацииКлиента", 
        ЭтотОбъект, 
        КлиентАвторизация);
    
    ОбработатьРезультатАвторизации(КлиентАвторизация, 
        ОписаниеЭтапаПолученияДанных, 
        ОписаниеСледующиегоЭтапаАвторизации);
      
КонецПроцедуры // ВыполнитьПослеАвторизацииПриложения()

&НаСервере
Функция АвторизироватьКлиента()
    
    ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
    РезультатАвторизации = ОбработкаОбъект.АвторизироватьКлиента(
        Login, Password, Id);
    ЗначениеВРеквизитФормы(ОбработкаОбъект, "Объект");
    
    Возврат РезультатАвторизации;    
        
КонецФункции // АвторизироватьКлиента()

После авторизации пользователя, необходимо проверить результат выполнения. Если валидация сессии прошла успешно и роль для вызова операции Web-сервера банка доступна, тогда выполняем запрос на получения полезных данных, иначе переходим на следующий этап авторизации.

ШАГ 3. Прохождение OTP-авторизации

// Процедура выполняется после успешной авторизации клиента и если необходимо
// пройти OTP-авторизацию клиент-банка.
// 
// Параметры:
//  ОписаниеЭтапаПолученияДанных - ОписаниеОповещения - оповещение которое будет выполнено если 
//                                                      авторизация будет успешной.
//  КлиентАвторизация            - Произвольный       - результат предыдущего этапа авторизации.
// 
&НаКлиенте
Процедура ВыполнитьПослеАвторизацииКлиента(ОписаниеЭтапаПолученияДанных,
    КлиентАвторизация = Неопределено) Экспорт
    
    Если ТипЗнч(КлиентАвторизация) = Тип("ФиксированнаяСтруктура") Тогда
        
        Если ТипЗнч(КлиентАвторизация.Message) = Тип("ФиксированныйМассив") Тогда
            
            ПараметрыФормы = Новый Структура("НомераТелефонов", 
                КлиентАвторизация.Message);
                            
            ОткрытьФорму("ВнешняяОбработка.Приват24.Форма.ФормаВыбораНомераТелефона", 
                ПараметрыФормы, 
                ЭтотОбъект,
                ,
                ,
                ,
                Новый ОписаниеОповещения(
                    "ВыполнитьПослеЗакрытияФормыВыбораТелефона", 
                    ЭтотОбъект, 
                    ОписаниеЭтапаПолученияДанных),
                РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);            
            
        Иначе
            
            ОткрытьФорму("ВнешняяОбработка.Приват24.Форма.ФормаOTP", 
                , 
                ЭтотОбъект,
                ,
                ,
                ,
                Новый ОписаниеОповещения(
                    "ВыполнитьПослеЗакрытияФормыOTP", 
                    ЭтотОбъект, 
                    ОписаниеЭтапаПолученияДанных),
                РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
        
        КонецЕсли;
        
    КонецЕсли;
              
КонецПроцедуры // ВыполнитьПослеАвторизацииКлиента()

&НаКлиенте
Процедура ВыполнитьПослеЗакрытияФормыВыбораТелефона(РезультатЗакрытия,
    ОписаниеЭтапаПолученияДанных = Неопределено) Экспорт
    
    Если ЗначениеЗаполнено(РезультатЗакрытия) Тогда
        
        OtpDev = РезультатЗакрытия;
        
        КлиентАвторизация = АвторизироватьКлиентаОтправитьOTP();    
        ОписаниеСледующиегоЭтапаАвторизации = Новый ОписаниеОповещения(
            "ВыполнитьПослеАвторизацииКлиента", 
            ЭтотОбъект, 
            КлиентАвторизация);
        
        ОбработатьРезультатАвторизации(КлиентАвторизация, 
            ОписаниеЭтапаПолученияДанных, 
            ОписаниеСледующиегоЭтапаАвторизации);
        
    КонецЕсли;
    
КонецПроцедуры // ВыполнитьПослеЗакрытияФормыВыбораТелефона() 

ШАГ 4. Проверка OTP-авторизации

На последнем этапе уже нет необходимости создавать описание оповещения для следующего этапе авторизации, этот уже последний.

&НаКлиенте
Процедура ВыполнитьПослеЗакрытияФормыOTP(РезультатЗакрытия,
    ОписаниеЭтапаПолученияДанных = Неопределено) Экспорт
    
    Если ЗначениеЗаполнено(РезультатЗакрытия) Тогда
        
        Otp = РезультатЗакрытия;
        
        КлиентАвторизация = АвторизироватьКлиентаПроверитьOTP();
        ОбработатьРезультатАвторизации(КлиентАвторизация,
            ОписаниеЭтапаПолученияДанных);
        
    КонецЕсли;
       
КонецПроцедуры // ВыполнитьПослеЗакрытияФормыOTP()

&НаСервере
Функция АвторизироватьКлиентаПроверитьOTP()
    
    ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
    РезультатАвторизации = ОбработкаОбъект.АвторизироватьКлиентаПроверитьOTP(
        Otp, Id);
    ЗначениеВРеквизитФормы(ОбработкаОбъект, "Объект");
    
    Возврат РезультатАвторизации;
        
КонецФункции // АвторизироватьКлиентаПроверитьOTP()

Если все прошло успешно, будут получены выписки из банка. В случае проблем, ЗначениеВРеквизитФормы использовалось для того что бы формировать вполне симпатичные логи:

10.09.2017 23:36:59:

REQUEST URL
Production Base URL: link.privatbank.ua
Operation: POST /api/auth/createSession

REQUEST BODY
{
"clientId": "*******",
"clientSecret": "*******"
}

10.09.2017 23:36:59:

RESPONSE
{"id":"*******","clientId":"*******","expiresIn":1505079419,"roles":["ROLE_CLIENT"]}

10.09.2017 23:36:59: Выполнен запрос авторизации приложения (279 мс).
10.09.2017 23:36:59: OK: Приложение успешно авторизировано.
10.09.2017 23:36:59:

REQUEST URL
Production Base URL: link.privatbank.ua
Operation: POST /api/p24BusinessAuth/createSession

REQUEST BODY
{
"login": "*******",
"password": "*******",
"sessionId": "*******"
}

10.09.2017 23:37:00:

RESPONSE
{"id":"*******","clientId":"*******","expiresIn":1505079419,"message":"Authentication successfull","roles":["ROLE_P24_BUSINESS","ROLE_CLIENT"]}

10.09.2017 23:37:00: Выполнен запрос авторизации клиента (1 129 мс).
10.09.2017 23:37:00: Authentication successfull
10.09.2017 23:37:00:

REQUEST URL
Production Base URL: link.privatbank.ua
Operation: GET /api/p24b/statements?acc=26006054710862&stdate=10.09.2017&endate=10.09.2017

10.09.2017 23:37:01:

RESPONSE
{}

10.09.2017 23:37:01: Выполнен запрос для получения выписок из банка (552 мс).
10.09.2017 23:37:01: OK: Выписки из банка за указанный период отсутствуют.

Вместо послесловия

Надеюсь, вы заметили шаблон, каждый этап авторизации имеет всего две процедуры или функции: первая процедура предназначена для создания описания оповещения следующего шага авторизации и вызова процедуры/функции/формы текущего шага обработчика, а в самом конце выполняется проверка успешности и переход к получению полезных данных или на следующий шаг авторизации.

Статья в личном блоге клац

См. также

Автоматическая обработка документов по банку при загрузке банковской выписки в 1С

Банковские операции Обмен с интернет-банком Эквайринг/ридер магнитных карт Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 Бухгалтерский учет Управленческий учет Платные (руб)

Устали вручную дозаполнять и корректировать документы по банку после загрузки банковской выписки? С помощью правил обработки банковской выписки вы сможете автоматически определять правильный вид хозяйственной операции, заполнять расшифровку, аналитику платежа и многое другое.

12000 руб.

21.03.2023    8181    32    12    

43

Обмен с клиентом банка для Беларуси

Банковские операции Обмен с интернет-банком Платформа 1С v8.3 Бухгалтерский учет Конфигурации 1cv8 1С:Конвертация данных 1С:Бухгалтерия 3.0 Беларусь Бухгалтерский учет Платные (руб)

Типовая обработка "Клиент-банк" из конфигурации 1С "Бухгалтерия для Беларуси, редакция 2.1" корректно работает с выписками только банка "Дабрабыт", до 28.01.2019 "Москва-Минск". А бухгалтеру нужно работать и с другими банками и с другими конфигурациями. Для этого было разработано расширение, которое позволит решить данную проблему!

10.10.2017    36391    69    Xershi    41    

57

SALE! 20%

Обмен с клиентом банка для БП 3.0, ERP 2.4, ERP 2.5 с дополнительными правилами загрузки

Обмен с интернет-банком Банковские операции Платформа 1С v8.3 Бухгалтерский учет 1С:Бухгалтерия 3.0 Россия Бухгалтерский учет Платные (руб)

Выписка клиент-банка загружается и обрабатывается по Вашим правилам. Заполнение реквизитов документов, анализ назначения платежа. Работа с клиент банком не отличается от типового варианта, а документы автоматически заполняются так, как нужно вам. Дополнительная обработка, работающая совместно с основным расширением позволяет подключать собственные произвольные алгоритмы.

20400 16320 руб.

03.04.2013    122258    243    377    

158

Загрузка банковской выписки из Excel и формирование файла формата 1CClientBankExchange (kl_to_1c)

Банковские операции Обмен с интернет-банком Загрузка и выгрузка в Excel Платформа 1С v8.3 1С:Бухгалтерия 2.0 1С:Управление торговлей 10 1С:Управление нашей фирмой 1.6 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Бухгалтерский учет Платные (руб)

Обработка создания файла по стандарту обмена с системами "Клиент банка" kl_to_1c.txt Формат файла обмена Российская Федерация, версия 1.03 При восстановлении учета в случае отсутствия в 1с банковских выписок (например, р/с в банке закрыт в связи с банкротством и доступ к выгрузке из клиент-банка невозможен), можно запросить у банка выписку за период в формате Excel. С помощью этой обработки создается файл формата 1CClientBankExchange, в 1с77 он назывался kl_to_1c.txt который загружается штатными средствами в 1с8. При этом будут созданы загружаемые контрагенты и их р/с.

3840 руб.

19.04.2019    41658    128    52    

62

Загрузка реестра платежей физических лиц из Сбербанка

Банковские операции Обмен с интернет-банком Бухгалтерский учет 1С:Бухгалтерия 3.0 Бухгалтерский учет Платные (руб)

Обработка загружает реестры платежей Сбербанка в документы "Поступление на расчетный счет". (Возможность покупки за рубли или StartMoney).

6000 руб.

16.07.2020    19255    33    10    

12
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. koshak84 9 13.09.17 12:53 Сейчас в теме
Сейчас как раз занимаюсь автоматической загрузкой выписок из 3х разных банков в старую-древнюю УПП. Посмотрел код реализации DirectBank в Библиотеке электронных документов и решил пойти другим путем. Просто в конфигурации БЭД написал обработку, которая с заданной периодичностью запрашивает документы из банков по уже готовым алгоритмам, а потом просто выгружает полученные файлы в УПП. Задача решилась малой кровью. А так да, смотреть без слез на то, что написано в БЭД не возможно.
2. bulpi 215 12.10.17 21:18 Сейчас в теме
Автор, надо бы выложить обработку. Иначе единственное, что понятно из этой статьи, так это то, что Вы очень умный, а я нет. Если это было целью, то Вы добились своего.
3. Inkasor 28 13.10.17 01:07 Сейчас в теме
(2)Это шаблон архитектуры, весь код с примерами прямо в статье, какая ещё обработка?
4. bulpi 215 13.10.17 09:28 Сейчас в теме
В коде есть ссылки на объекты, например, Роли. В коде есть ссылки на формы обработки. Без обработки это не код, а сочинение на вольную тему. Представьте, что Вы читатель статьи, и Вам нужно что-то сделать. Как ? Впрочем, хозяин-барин.
5. pbazeliuk 1955 13.10.17 10:15 Сейчас в теме
(4) Роли это простая структура которая согласуется с описанием АPI (https://link.privatbank.ua/console/wiki/p24business_auth), это не роли конфигурации.
"Объект" это объект обработки, основной реквизит формы.
Каждый переход в модуль обработки, это обычный HTTP запрос к сервису банка и преобразование ответа в структуру\массив, цель статьи не дать готовое решение, а подумать и развить аналитические навыки.

Вот, Петр Цап, тоже написал статью над которой необходимо было подумать, и там ни строчки кода, но есть классные идеи.
6. Inkasor 28 13.10.17 15:31 Сейчас в теме
(5) Спасибо на добром слове :) Я ваши статьи тоже сейчас активно осмысливаю на предмет практического применения, вот эта была прямо вообще в тему.
Оставьте свое сообщение