Исходные данные:
1. имеется мобильное приложение на swift и приложение опубликовано в appStore
2. имеется сертификат .p8 для push уведомлений
Задача: отправить в мобильное приложение Push уведомление средствами 1С8 по протоколу http2
Примечание: используется средства Windows. Для работы под Linux потребуется правка
Подзадача 1: Проверить работоспособность сертификата, мобильного приложения, настроек личного кабинета НЕ средствами 1С8.
Для тестирования можно воспользоваться имеющимися онлайн услугами, но мне в этом не повезло. Уведомления по сертификату .p8 не проходили. В конечном итоге проверку выполнил средствами командной строки MacOS
TEAM_ID=922L57LJJB
TOKEN_KEY_FILE_NAME=/Users/dima/Desktop/p8/AuthKey_7W9KQGUTC7.p8
AUTH_KEY_ID=7W9KQGUTC7
TOPIC=ru.iReport24
DEVICE_TOKEN=eac391331d4d2638cfeca264c3a47c4a5d01849fbf449639f8982f001a4d57bb
APNS_HOST_NAME=api.push.apple.com
Здесь в переменных задаются параметры мобильной программы из личного кабинета Apple Developer. Указывается путь где храниться сертификат. Также указывается токен мобильного устройства.
Примечание 1: токен мобильного устройства меняется при переустановке программы. При обновлении developer версии программы на Production, токен так же обновляется
Примечание 2: в моём примере указан путь "api.push.apple.com" для версии production. Если вы работаете с developer поменяйте на api.sandbox.push.apple.com
JWT_ISSUE_TIME=$(date +%s)
JWT_HEADER=$(printf '{ "alg": "ES256", "kid": "%s" }' "${AUTH_KEY_ID}" | openssl base64 -e -A | tr -- '+/' '-_' | tr -d =)
JWT_CLAIMS=$(printf '{ "iss": "%s", "iat": %d }' "${TEAM_ID}" "${JWT_ISSUE_TIME}" | openssl base64 -e -A | tr -- '+/' '-_' | tr -d =)
JWT_HEADER_CLAIMS="${JWT_HEADER}.${JWT_CLAIMS}"
JWT_SIGNED_HEADER_CLAIMS=$(printf "${JWT_HEADER_CLAIMS}" | openssl dgst -binary -sha256 -sign "${TOKEN_KEY_FILE_NAME}" | openssl base64 -e -A | tr -- '+/' '-_' | tr -d =)
AUTHENTICATION_TOKEN="${JWT_HEADER}.${JWT_CLAIMS}.${JWT_SIGNED_HEADER_CLAIMS}"
Здесь вычисляется Токен для авторизации подключения к сайту. Просто копируете, вставляете текст в командную строку и выполняете. В переменной AUTHENTICATION_TOKEN будет необходимый токен.
Примечание 1: Для расчета токена используется время компьютера. При выполнении команды часы на компьютере должны быть синхронизированы.
Примечание 2: Срок жизни токена 60 минут. Затем требуется заново выполнить расчет.
curl -v --header "apns-topic: $TOPIC" --header "apns-push-type: alert" --header "authorization: bearer $AUTHENTICATION_TOKEN" --data '{"aps" : {"alert" : "тестовое сообщение", "badge" : 9, "sound" : "bingbong.aiff" }}' --http2 https://${APNS_HOST_NAME}/3/device/${DEVICE_TOKEN}
Отправка Push уведомления '{"aps" : {"alert" : "тестовое сообщение", "badge" : 9, "sound" : "bingbong.aiff" }}'
alert - текст сообщения
badge - цифра для передачи. Обычно используется для вывода кол-ва на иконке
sound - звук при получения уведомления
Подзадача 2: Реализация в 1С8 на Windows
1. Для реализации потребуется поставить OpenSSL и указать в окружении Windows путь к файлу openssl.exe
2. У меня в Windows 10 встроенная команда curl версии 7.83.1 не поддерживает протокол http2. Поэтому скачал с интернета пакет curl версии 7.85.0 и разместил его в папку D:\Curl\bin
&НаКлиенте
Процедура Команда1(Команда)
DEVICE_TOKEN="eac391331d4d2638cfeca264c3a47c4a5d01849fbf449639f8982f001a4d57bb";
Сообщение = "тестовое сообщение";
Цифра = 9;
Сообщение = убратьОпасныеСимволы(Сообщение); // с этим можно поспорить, но добавил для вредности
текстСообщения = получитьТекстСообщения(Сообщение, Цифра); // Преобразование текста сообщения в JSON строку
послатьПушНаСервере(Цифра, текстСообщения, DEVICE_TOKEN); // отправка уведомления
КонецПроцедуры
&НаСервере
функция убратьОпасныеСимволы(Сообщение)
// защитим текст
Сообщение = СтрЗаменить(сообщение, """", "");
Сообщение = СтрЗаменить(сообщение, "'", " ");
Сообщение = СтрЗаменить(сообщение, Символы.ПС, " ");
Сообщение = СтрЗаменить(сообщение, Символы.ПФ, " ");
Сообщение = СтрЗаменить(сообщение, Символы.НПП, " ");
Сообщение = СтрЗаменить(сообщение, Символы.ВК, " ");
Сообщение = СтрЗаменить(сообщение, Символы.ВТаб, " ");
возврат сообщение;
конецфункции
&НаСервере
функция получитьТекстСообщения(Сообщение, Цифра)
ЗаписьJSON = Новый ЗаписьJSON;
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет,,Истина,,истина,истина,,,Истина); // максимально закрываем
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписиJSON);
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьИмяСвойства("aps");
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьИмяСвойства("alert");
ЗаписьJSON.ЗаписатьЗначение(""+сообщение+"");
ЗаписьJSON.ЗаписатьИмяСвойства("badge");
ЗаписьJSON.ЗаписатьЗначение(цифра);
ЗаписьJSON.ЗаписатьИмяСвойства("sound");
ЗаписьJSON.ЗаписатьЗначение("bingbong.aiff");
ЗаписьJSON.ЗаписатьКонецОбъекта();
ЗаписьJSON.ЗаписатьКонецОбъекта();
СтрокаТелоЗапроса = ЗаписьJSON.Закрыть();
возврат СтрокаТелоЗапроса;
конецфункции
&НаСервере
Процедура послатьПушНаСервере(Цифра, текстСообщения, DEVICE_TOKEN)
// настроим параметры
TEAM_ID="922L57LJJB";
//TOKEN_KEY_FILE_NAME="/Users/dimaermilov/Desktop/p8/AuthKey_7W9KQGUTC7.p8"; // сертификат будем брать из макета
AUTH_KEY_ID="7W9KQGUTC7";
TOPIC="ru.iReport24";
APNS_HOST_NAME="api.push.apple.com";
// вычислим токен
JWT_ISSUE_TIME=Формат(ТекущаяДата() - дата(1970,1,1,3,0,0), "ЧГ=0"); // приведение текущего формата к Unix. !! Обязательно сверьте со значением из MacOs. Пример из интернета дата(1970,1,1,1,0,0) давал расхождение в два часа.
JWT_HEADER=получитьBase64("{ ""alg"": ""ES256"", ""kid"": """+AUTH_KEY_ID+""" }"); // приведем средствами 1С
JWT_CLAIMS=получитьBase64("{ ""iss"": """+TEAM_ID+""", ""iat"": "+JWT_ISSUE_TIME+" }");
JWT_HEADER_CLAIMS = JWT_HEADER+"."+JWT_CLAIMS;
JWT_SIGNED_HEADER_CLAIMS = ПодписатьСтроку(JWT_HEADER_CLAIMS); // средствами 1С8 эту задачу решить не смог. Заранее установил openssl и прописал путь в окружении windows
AUTHENTICATION_TOKEN=JWT_HEADER+"." +JWT_CLAIMS+"."+JWT_SIGNED_HEADER_CLAIMS;
// отправим push
ОтправитьPush(Цифра, текстСообщения, DEVICE_TOKEN, AUTHENTICATION_TOKEN, TOPIC, APNS_HOST_NAME);
конецпроцедуры
&НаСервере
функция получитьBase64(ИсходныеДанные)
Поток = Новый ПотокВПамяти;
ЗаписьТекста = Новый ЗаписьТекста(Поток) ;
ЗаписьТекста.Записать(ИсходныеДанные);
ЗаписьТекста.Закрыть();
ДвоичныеДанные = Поток.ЗакрытьИПолучитьДвоичныеДанные();
Строка64 = Base64Строка(ДвоичныеДанные);
возврат(Строка64);
конецфункции
&НаСервере
функция ПодписатьСтроку(JWT_HEADER_CLAIMS)
// сохраним сертификат во временный файл
ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
ДвоичныеДанные = ОбработкаОбъект.ПолучитьМакет("AuthKey_7W9KQGUTC7_p8"); // в конфигураторе сохраним сертификат как двоичные данные в макет. Лучше хранить сертификат и в регистре сведений. Это позволит отдавать программный код сторонним разработчикам.
ИмяФайлаКлюча = ПолучитьИмяВременногоФайла("key");
ДвоичныеДанные.Записать(ИмяФайлаКлюча);
// сохраним строку во временный файл
ИмяФайлаСтроки = ПолучитьИмяВременногоФайла("txt");
ФайлTXT= Новый ТекстовыйДокумент;
ФайлTXT.УстановитьТекст(JWT_HEADER_CLAIMS);
ФайлTXT.Записать(ИмяФайлаСтроки, КодировкаТекста.Системная);
// запустим openssl подпишем 256
ИмяФайлаРезультата256 = ПолучитьИмяВременногоФайла("256");
команднаяСтрока = "openssl dgst -sha256 -sign "+ИмяФайлаКлюча+" -out "+ИмяФайлаРезультата256+" "+ИмяФайлаСтроки;
ЗапуститьПриложение(команднаяСтрока,,истина);
// запустим openssl и закодируем base64
ИмяФайлаРезультата64 = ПолучитьИмяВременногоФайла("64");
команднаяСтрока = "openssl base64 -in "+ИмяФайлаРезультата256+" -out "+ИмяФайлаРезультата64+" -e -A ";
ЗапуститьПриложение(команднаяСтрока,,истина);
// получим результат
ТекстовыйФайл = Новый ТекстовыйДокумент;
ТекстовыйФайл.Прочитать(ИмяФайлаРезультата64);
результат = ТекстовыйФайл.ПолучитьТекст();
// удалим временные файлы
УдалитьФайлы(ИмяФайлаКлюча);
УдалитьФайлы(ИмяФайлаСтроки);
УдалитьФайлы(ИмяФайлаРезультата256);
УдалитьФайлы(ИмяФайлаРезультата64);
// подпишем base64 и вернем результат
возврат результат;
конецфункции
&НаСервере
процедура ОтправитьPush(Цифра, текстСообщения, DEVICE_TOKEN, AUTHENTICATION_TOKEN, TOPIC, APNS_HOST_NAME)
// отправить средствами 1с8 не получилось. Сервер apple требует протокол http2. На платформе 8.3.21.1484 подключение не осуществлялось
// Программа тестировалась на Windows 10 и curl 7.83.1 не поддерживает http2.
// с интернета скачал curl curl 7.85.0 и разместил в папке D:\curl\bin
СистемнаяИнформация = Новый СистемнаяИнформация;
ЭтоWindowsКлиент = СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86
ИЛИ СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86_64;
// команднаяСтрокаДляПроверкиНаМас - её можно просто скопировать и вставить в MacOS для проверки
команднаяСтрокаДляПроверкиНаМас = "curl -v"
+ " --header ""apns-topic: "+TOPIC + """"
+ " --header ""apns-push-type: alert"""
+ " --header ""authorization: bearer "+AUTHENTICATION_TOKEN + """"
+ " --data '"+текстСообщения+"'"
+ " --http2 https://"+APNS_HOST_NAME+"/3/device/"+DEVICE_TOKEN;
Если ЭтоWindowsКлиент тогда
// для Windows нужно помучиться
текстСообщения = СтрЗаменить(текстСообщения, """", "\"""); // в windows защитим все кавычки косой чертой \ // в json строке защищаем кавычки
команднаяСтрокаWin = "curl -v"
+ " --header ""apns-topic: "+TOPIC + """"
+ " --header ""apns-push-type: alert"""
+ " --header ""authorization: bearer "+AUTHENTICATION_TOKEN + """"
+ " --data """+текстСообщения+""""
+ " --http2 https://"+APNS_HOST_NAME+"/3/device/"+DEVICE_TOKEN;
команднаяСтрокаWin = "D:\curl\bin\"+команднаяСтрокаWin; // в лоб прописал путь к скачанной curl
// ЗапуститьПриложение("D:\curl\bin\"+команднаяСтрокаWin,,истина); // если в сообщении будет кириллица, то сообщение не дойдет
// создадим .bat файл в кодировке utf-8
ИмяФайлаBat = ПолучитьИмяВременногоФайла("bat");
// записываем в файл с символами BOM в начале файле
ТекстовыйФайлUTF8_Bom = Новый ТекстовыйДокумент();
ТекстовыйФайлUTF8_Bom.ДобавитьСтроку("chcp 1251"); // поменяем окружение с 866 dos на 1251
ТекстовыйФайлUTF8_Bom.ДобавитьСтроку(команднаяСтрокаWin);
ТекстовыйФайлUTF8_Bom.Записать(ИмяФайлаBat,"UTF-8"); // при сохранении в UTF-8 добавляется BOM символы в начале файла
// открываем файл и считываем символы после символов BOM
Данные = Новый ДвоичныеДанные(ИмяФайлаBat);
Строка64=Base64Строка(Данные);
Строка64=Прав(Строка64,СтрДлина(Строка64)-4); // убираем BOM символы
ДанныеНаЗапись=Base64Значение(Строка64);
ДанныеНаЗапись.Записать(ИмяФайлаBat); // записываем
ЗапуститьПриложение(ИмяФайлаBat,,истина); // Отправка push уведомления
УдалитьФайлы(ИмяФайлаBat);
конецесли;
// для отладки в командной строке
сообщить("Строка сгенерированная на Windows для Mac");
сообщить(команднаяСтрокаДляПроверкиНаМас);
сообщить("Строка сгенерированная для Windows");
сообщить(команднаяСтрокаWin);
конецпроцедуры