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

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

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

В наличии

Многим из читателей знакомы понятия стандартов разработки и шаблонов проектирования. Для платформы «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: Выписки из банка за указанный период отсутствуют.

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

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

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

has been added to your cart:
Оформление заказа