Безопасные push-уведомления посредством центров уведомлений Azure

Обзор

Поддержка push-уведомлений в Microsoft Azure позволяет получить доступ к простой, многоплатформенной и масштабируемой инфраструктуре для отправки push-уведомлений. Она значительно упрощает реализацию push-уведомлений как для объекта-получателя, так и для корпоративных приложений для мобильных платформ.

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

На высоком уровне поток можно представить следующим образом.

  1. Серверная часть приложения:
    • Сохраняет полезную нагрузку в базе данных серверной части.
    • Отправляет идентификатор этого уведомления устройству (защищаемые сведения не передаются).
  2. Приложение на устройстве при получении уведомления:
    • Устройство связывается с серверной частью и запрашивает полезную нагрузку.
    • Приложение может показывать полезную нагрузку в виде уведомления на устройстве.

Стоит отметить: в предыдущем потоке (и в этом учебнике) мы предположили, что устройство сохраняет маркер проверки подлинности в локальном хранилище после входа пользователя. Таким образом обеспечивается удобство работы, так как с помощью этого маркера устройство способно получать безопасные полезные данные уведомлений. Если приложение не сохраняет маркеры проверки подлинности на устройстве или если истек срок действия маркеров, приложение устройства должно отобразить общее сообщение после получения уведомления, предлагая пользователю запустить приложение. Затем приложение выполняет проверку подлинности пользователя и отображает полезную нагрузку уведомления.

В этом учебнике по безопасности push-уведомлений показано, как безопасно отправлять push-уведомление. Данное руководство является продолжением другого учебника под названием Уведомление пользователей , поэтому необходимо сначала выполнить шаги в указанном учебнике.

Примечание

В этом руководстве предполагается, что вы создали и настроили центр уведомлений, как описано в статье Отправка push-уведомлений в приложения iOS с помощью службы "Центры уведомлений Azure".

Проект веб-интерфейса API

  1. В Visual Studio откройте проект AppBackend , созданный в учебнике Уведомление пользователей .

  2. В файле замените Notifications.cs целый класс Уведомления на следующий код. Убедитесь, что заменили заполнители на строку подключения (с полным доступом) для центра уведомлений и имени центра. Эти значения можно получить на портале Azure. В этом модуле теперь представлены разные уведомления безопасности, которые будут отправлены. При полной реализации уведомления будут сохранены в базе данных; для упрощения в этом случае мы сохраняем их память.

     public class Notification
     {
         public int Id { get; set; }
         public string Payload { get; set; }
         public bool Read { get; set; }
     }
    
     public class Notifications
     {
         public static Notifications Instance = new Notifications();
    
         private List<Notification> notifications = new List<Notification>();
    
         public NotificationHubClient Hub { get; set; }
    
         private Notifications() {
             Hub = NotificationHubClient.CreateClientFromConnectionString("{conn string with full access}",     "{hub name}");
         }
    
         public Notification CreateNotification(string payload)
         {
             var notification = new Notification() {
             Id = notifications.Count,
             Payload = payload,
             Read = false
             };
    
             notifications.Add(notification);
    
             return notification;
         }
    
         public Notification ReadNotification(int id)
         {
             return notifications.ElementAt(id);
         }
     }
    
  3. Замените в файле NotificationsController.cs код в определении класса NotificationsController на следующий. Этот компонент реализует способ безопасного получения уведомления устройством, а также способ (в обучающих целях) активации безопасного push-уведомления для устройств. Обратите внимание, что при отправке уведомления в центр уведомлений мы отправляем только необработанное уведомление с идентификатором уведомления (без фактического сообщения):

     public NotificationsController()
     {
         Notifications.Instance.CreateNotification("This is a secure notification!");
     }
    
     // GET api/notifications/id
     public Notification Get(int id)
     {
         return Notifications.Instance.ReadNotification(id);
     }
    
     public async Task<HttpResponseMessage> Post()
     {
         var secureNotificationInTheBackend = Notifications.Instance.CreateNotification("Secure confirmation.");
         var usernameTag = "username:" + HttpContext.Current.User.Identity.Name;
    
         // windows
         var rawNotificationToBeSent = new Microsoft.Azure.NotificationHubs.WindowsNotification(secureNotificationInTheBackend.Id.ToString(),
                         new Dictionary<string, string> {
                             {"X-WNS-Type", "wns/raw"}
                         });
         await Notifications.Instance.Hub.SendNotificationAsync(rawNotificationToBeSent, usernameTag);
    
         // apns
         await Notifications.Instance.Hub.SendAppleNativeNotificationAsync("{\"aps\": {\"content-available\": 1}, \"secureId\": \"" + secureNotificationInTheBackend.Id.ToString() + "\"}", usernameTag);
    
         // gcm
         await Notifications.Instance.Hub.SendGcmNativeNotificationAsync("{\"data\": {\"secureId\": \"" + secureNotificationInTheBackend.Id.ToString() + "\"}}", usernameTag);
    
         return Request.CreateResponse(HttpStatusCode.OK);
     }
    

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

  1. После этого повторно развернем это приложение на веб-сайте Azure, чтобы сделать его доступным для всех устройств. Щелкните правой кнопкой мыши проект AppBackend и нажмите кнопку Опубликовать.
  2. Выберите в качестве цели публикации веб-сайт Azure. Выполните вход с помощью учетной записи Azure, выберите существующий или новый веб-сайт и запишите значение свойства URL-адрес назначения на вкладке Подключение. Далее в учебнике этот URL-адрес будет называться конечной точкой серверной части. Нажмите кнопку Опубликовать.

Внесение изменений в проект iOS

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

Для достижения этой цели мы напишем логику извлечения безопасного содержимого из серверной части приложения.

  1. Убедитесь, что в AppDelegate.m приложение регистрируется на автоматические уведомления и обрабатывает отправленный из серверной части идентификатор уведомления. Добавьте параметр UIRemoteNotificationTypeNewsstandContentAvailability в didFinishLaunchingWithOptions.

    [[UIApplication sharedApplication] registerForRemoteNotificationTypes: UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeNewsstandContentAvailability];
    
  2. В начало файла AppDelegate.m добавьте раздел реализации со следующим объявлением:

    @interface AppDelegate ()
    - (void) retrieveSecurePayloadWithId:(int)payloadId completion: (void(^)(NSString*, NSError*)) completion;
    @end
    
  3. Затем добавьте в реализационную часть следующий код, заменив заполнитель {back-end endpoint} на конечную точку серверной части, которая была получена ранее:

    NSString *const GetNotificationEndpoint = @"{back-end endpoint}/api/notifications";
    
    - (void) retrieveSecurePayloadWithId:(int)payloadId completion: (void(^)(NSString*, NSError*)) completion;
    {
        // check if authenticated
        ANHViewController* rvc = (ANHViewController*) self.window.rootViewController;
        NSString* authenticationHeader = rvc.registerClient.authenticationHeader;
        if (!authenticationHeader) return;
    
        NSURLSession* session = [NSURLSession
                                    sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                                    delegate:nil
                                    delegateQueue:nil];
    
        NSURL* requestURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%d", GetNotificationEndpoint, payloadId]];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"GET"];
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@", authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
    
        NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
            if (!error && httpResponse.statusCode == 200)
            {
                NSLog(@"Received secure payload: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    
                NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:data options: NSJSONReadingMutableContainers error: &error];
    
                completion([json objectForKey:@"Payload"], nil);
            }
            else
            {
                NSLog(@"Error status: %ld, request: %@", (long)httpResponse.statusCode, error);
                if (error)
                    completion(nil, error);
                else {
                    completion(nil, [NSError errorWithDomain:@"APICall" code:httpResponse.statusCode userInfo:nil]);
                }
            }
        }];
        [dataTask resume];
    }
    

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

  4. Теперь обработайте входящее уведомление и воспользуйтесь вышеуказанным методом, чтобы извлечь содержимое для отображения. Во-первых, мы должны разрешить приложению iOS возможность работы в фоновом режиме при получении push-уведомления. В XCode выберите проект приложения в панели слева, затем выберите основную платформу в разделе Targets (Целевые объекты) центральной панели.

  5. Затем щелкните вкладку Capabilities (Возможности) в верхней части центральной панели и установите флажок Remote Notifications (Удаленные уведомления).

    Снимок экрана XCode с выбранным проектом приложения и открытой вкладкой

  6. В файле AppDelegate.m добавьте следующий метод для обработки push-уведомлений.

    -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
    {
        NSLog(@"%@", userInfo);
    
        [self retrieveSecurePayloadWithId:[[userInfo objectForKey:@"secureId"] intValue] completion:^(NSString * payload, NSError *error) {
            if (!error) {
                // show local notification
                UILocalNotification* localNotification = [[UILocalNotification alloc] init];
                localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:0];
                localNotification.alertBody = payload;
                localNotification.timeZone = [NSTimeZone defaultTimeZone];
                [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
    
                completionHandler(UIBackgroundFetchResultNewData);
            } else {
                completionHandler(UIBackgroundFetchResultFailed);
            }
        }];
    
    }
    

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

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

Для запуска приложения выполните следующие действия:

  1. В XCode запустите приложение на физическом устройстве под управлением iOS (push-уведомления не будут работать в симуляторе).
  2. В пользовательском интерфейсе приложения iOS введите имя пользователя и пароль. Это могут быть любые совпадающие строки.
  3. В пользовательском интерфейсе приложения iOS нажмите Вход. Затем нажмите Отправить push-уведомление. Вы должны увидеть конфиденциальное уведомление, которое будет отображено в центре уведомлений.