Сертификат для Джонни

—

от автора

Я человек далекий от программирования, живу за колючкой и люблю бить баклуши. Но случилось непредвиденное, моему сербскому другу Джонни, приехавшему в гости, понадобился QR-код. Пришлось расчехлить клавиатуру.

Краткое содержание первой серии

Ко мне приехал мой сербский приятель Никола Павлович, вакцинированный2 Пфайзером. В нашей стране его всюду просили показать QR-код или гуляй мимо. Ситуация идиотская. Человек дважды привит Пфайзером, а его никуда не пускают. Я был в бешенстве.

-Джонни, сгенерю-ка я тебе QR-код, — подумал я и сгенерил.

Как действует QR-код

Чтобы понять сценарий верификации кода, пришлось отправиться в местную Плазу (привет Виктору Левашову) и предъявить охраннику свой QR-сертификат. Откуда у меня сертификат? А я вакцинирован Спутником V. И вот что я обнаружил

  • охранник включил телефон

  • охранник включил камеру на телефоне

  • навел камеру на мой QR-код

  • нажал на всплывающий поп-ап

  • Проходи!

На экране телефона охранника высветилась информация обо мне в стыдливо прикрытом виде

Sic! В QR-коде просто зашифрован линк-ссылка на сайт Госуслуг. В чьей-то голове сформулировалось техническое задание о разработке приложения под iOS. Голова оказалась моя. И заметьте, информация на экране приложения не является персональной. Очевидно, что аббревиатура ФИО и 3 последние цифры паспорта никак не идентифицируют человека. По этим данным даже пол гражданина (гражданки?) не определить.

Техническое задание

  • Надо сделать сайт-пародию (дюжина строк кода на PHP) который отображает информацию из строки url-запроса типа http://govuslugi.her/vam_qr.php?string=BBB_15031964_22345

  • Надо сделать iOS приложение (пару дюжин строк на swift), которое будет генерить QR-код и показывать его на экране iPhone.

Серверная часть она же Web

Вообще, публикация планировалась как учебник по SwiftUI+MVVM+Reactive, но для общего понимания задачи приведу прототип серверной части.

Я сделал веб-страничку на PHP, она будет отображать входящую информацию, записанную в параметре name нашего url-запроса. Типа www.fig.vam/vsem.php?name=jonny_depp

Я извиняюсь, последний раз я кодировал на пе-хе-пе в 2005 году, поэтому прошу прощения за олд-стайл. Слава Джобсу, в PHP не надо проверять строку на её существование и на наличие в ней символов в нужных местах, код никогда не ломается и это прекрасно. Ой, тухлые помидоры!

if (isset($_GET['name'])) $name=($_GET['name']); else $name="Xerr"; list($fio, $birth, $pass) = explode("_", $name); $user = m_substr($fio, 0, 1, 'UTF-8')."***** ".m_substr($fio, 1, 1, 'UTF-8')."*****  ".mb_substr($fio, 2, 1, 'UTF-8')."********";  $passport = "Паспорт: ".substr($pass, 0, 2)."*****".substr($pass, 2, 3);     $birthday = "Дата рождения: ".$birth;

Как видите, мы получили 3 нужные переменные, которые будем отображать на веб-страничке. П-последний раз я кодировал на Х-Т-М-Л в 1997 году, поэтому извините за олд-олд-стайл из прошлого тысячелетия.

echo ' <div class="top"><strong> <span style="font-size:140%;color:#FF0000">QR</span> <span style="font-size:140%;color:#6666CC">УСЛУГИ</span></strong> <span style="color:#ffffff">BASHNI.ORG</span> <span style="font-size:140%;color:#000000">RUS</span></div> <div class="relative">СЕРТИФИКАТ О ГЕНЕРАЦИИ<br>QR-КОДА-19 <div class="absolute">Действителен</div> <br><p>№ QR9290499831</p></div><div class="info"><br>'.$user.'</div> <div class="info">'.$port.'</div> <div class="info">Дата рождения: '.$birth.'</div> <div class="close">Закрыть</div>';

Стили я опустил, их можно посмотреть на гитхабе. Они тупые и загадочные, как <див>ы из древних сказок Востока.

SwiftUI+MVVM

Переходим к приложению под iOS. Прости, google. Выбираем паттерн проектирования MVVM. Разумеется, для нашей маленькой задачи он избыточен, но потренироваться всегда полезно. Вдруг интервью на сеньора?? Я готов, если что, звоните от $4000. Телефон зашит в статье.

Модель содержит простейшую структуру из строки, которую надо где-то превратить в код-картинку и логическую переменную, говорящую что превращение совершилось/не совершилось.

struct UserModel {     var user: User  mutating func updateUser(name:String, birth:String, pass:String) {         let d = "_"         let temp = name+d+birth+d+pass         self.user = User(isValid: true, string: temp )     }  mutating func cleanUser() {         self.user = User(isValid: false, string: "")     }      struct User {         var isValid:Bool         var string:String         func genUrl()->String {             return urlPath+self.string         }     } } 

Полный текст файла ModelView.swift можно посмотреть на гитхабе. Вторая часть нашего паттерна — файл ContentView.swift. Он рисует в одном окне InputView ввод строки и кнопки, а во втором окне QRView изображение QR-кода. Этот SwiftUI стиль и он очень информативен — не нужны XIB-ы, верстка идет прямо в коде в джава-стиле.

import SwiftUI  struct ContentView: View {     @ObservedObject var model: ModelView     var body: some View {         if model.isValid() {             QRView(model: model)         } else {             InputView(model: model)         }     } }   struct InputView: View {     var model: ModelView     @State private var name: String = ""     @State private var birth: String = ""     @State private var passport: String = ""          var body: some View {         VStack {             Spacer()             TextField("3 буквы ФИО", text: $name)             TextField("ДР 15.03.1964", text: $birth)             TextField("2+3 цифры паспорта", text: $passport)             Spacer()             HStack {                 Button("Сделай QR", action: {                     self.model.tapQRButton(name, birth, passport)                 })                 Spacer()                 Button("Очистить", action: {                     name=""                     birth = ""                     passport = ""                     self.model.tapClean()                 })             }.padding()             Spacer()         }         .padding()         .textFieldStyle(RoundedBorderTextFieldStyle())     } }   struct QRView: View {     var model: ModelView     @State private var isSharePresented: Bool = false     var body: some View {         VStack {             if let image = model.generateQRCode() {                 Text("QR код").bold()                 Text("успешно сформирован")                 Spacer()                 Image(uiImage: image).resizable()                     .frame(width: 200, height: 200)             } else {                 Text("QR код").bold().foregroundColor(.red)                 Text("не сформирован").foregroundColor(.red)                 Spacer()                 Image(systemName: "qrcode").resizable()                     .frame(width: 75, height: 75)             }             Spacer()             Button("Назад", action: {           self.model.tapClean()             } ).padding()         }.padding()         Spacer()     } }          struct ContentView_Previews: PreviewProvider {         static var previews: some View {             ContentView(model: ModelView())         }     } 

И, наконец, последний акт Мерлезонского балета — это файл ModelView.swift, который одним глазом глядит на ContentView и получает от него сообщения о нажатии на кнопку, а другим глазом он глядит на UserModel, куда он складывает полученную от ContentView информацию и забирает строку, которую превращает в QR-картинку и возвращает в ContentView.

// //  ModelView.swift //  import UIKit  class ModelView: ObservableObject {     @Published private var model:UserModel = UserModel()      func isValid()->Bool {         return model.user.isValid     }      func tapQRButton(_ name:String,_ birth:String,_ pass:String) {         model.updateUser(name: name, birth: birth, pass: pass)     }       func tapClean() {         model.cleanUser()     }          func generateQRCode() -> UIImage? {         var uiImage: UIImage? //        let string = model.user.genUrl()         let string = "https://github.com/PapaBubaDiop/qrushka"          if let data = string.data(using: String.Encoding.utf8) {             if let filter = CIFilter(name: "CIQRCodeGenerator",                                      parameters: ["inputMessage": data,                                                   "inputCorrectionLevel": "L"]) {                 if let outputImage = filter.outputImage,                    let cgImage = CIContext().createCGImage(outputImage,                                                            from: outputImage.extent) {                     let size = CGSize(width: outputImage.extent.width * 5.0,                                       height: outputImage.extent.height * 5.0)                     UIGraphicsBeginImageContext(size)                     if let context = UIGraphicsGetCurrentContext() {                         context.interpolationQuality = .none                         context.draw(cgImage,                                      in: CGRect(origin: .zero,                                                 size: size))                         uiImage = UIGraphicsGetImageFromCurrentImageContext()                     }                     UIGraphicsEndImageContext()                 }             }         }         return uiImage     } }

ModelView объявлена внутри ContentView реактивной

@ObservedObject var model: ModelView 

и это значит, что графические окна перерисовываются каждый раз когда меняется содержимое нашей модели. Еще раз сценарий нашего приложения

  • вводим текст и нажимаем кнопку в ContentView

  • ModelView получает этот текст и обновляет UserModel и значит меняется сам

  • UserModel формирует из текста url виде строки

  • раз UserModel изменился, то и ModelView изменился, значит ContentView перерисовывается в соотвествии с новыми данными, которые появились в ModelView

Ура, проект готов и компилируется. No Errors No Warnings.

Запускаем приложение в симуляторе

И получаем стартовый экран. Заполняем поля в соответсвии с документом Джонни.

Нажимаем кнопку Сделай QR и получаем QR-картинку, в которой скрыт линк на наш сайт, где уже лежит php-скрипт, описанный выше. Сформированные картинки, граждане, настоящие!

Вы можете навести свой девайс на картинку и протестировать ее. Мою протестировал тот же охранник из Плазы. Welcome to Russia, Джонни!

Внимание! Никогда так не делайте! Не используйте прототип в незаконных целях! Потому что скоро это будет весить от 3-х до 5-ти.

Заключение

Вся эта история, разумеется, вымышлена от начала и до конца, но код, товарищи, настоящий! Ссылка на гитхаб прилагается.

Задачка

QR-код формируется разной зернистости в зависимости от длины строки. Мое приложение зашифровало в коды число 0 и число 1. Угадайте, где какой QR-код для 0 и 1?


ссылка на оригинал статьи https://habr.com/ru/articles/591483/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *