Azure 通知中心安全推送Azure Notification Hubs Secure Push


利用 Azure 中的推送通知支持,可以访问易于使用且向外扩展的多平台推送基础结构,这大大简化了为移动平台的使用者应用程序和企业应用程序实现推送通知的过程。Push notification support in Azure enables you to access an easy-to-use, multiplatform, scaled-out push infrastructure, which greatly simplifies the implementation of push notifications for both consumer and enterprise applications for mobile platforms.

由于法规或安全约束,有时应用程序可能想要在通知中包含某些无法通过标准推送通知基础结构传输的内容。Due to regulatory or security constraints, sometimes an application might want to include something in the notification that cannot be transmitted through the standard push notification infrastructure. 本教程介绍如何通过客户端设备和应用后端之间安全且经过验证的连接发送敏感信息,以便获得相同的体验。This tutorial describes how to achieve the same experience by sending sensitive information through a secure, authenticated connection between the client device and the app backend.

在高级别中,此流程如下所示:At a high level, the flow is as follows:

  1. 应用后端:The app back-end:
    • 在后端数据库中存储安全有效负载。Stores secure payload in back-end database.
    • 将此通知的 ID 发送到此设备(不发送任何安全信息)。Sends the ID of this notification to the device (no secure information is sent).
  2. 此设备上的应用在接收通知时:The app on the device, when receiving the notification:
    • 此设备将联系请求安全有效负载的后端。The device contacts the back-end requesting the secure payload.
    • 此应用可以将有效负载显示为设备上的通知。The app can show the payload as a notification on the device.

请务必注意,在之前的流程(以及本教程中)中,我们假设此设备会在用户登录后在本地存储中存储身份验证令牌。It is important to note that in the preceding flow (and in this tutorial), we assume that the device stores an authentication token in local storage, after the user logs in. 这可以保证无缝的体验,因为该设备可以使用此令牌检索通知的安全有效负载。This guarantees a seamless experience, as the device can retrieve the notification’s secure payload using this token. 如果应用程序未在设备上存储身份验证令牌,或者如果这些令牌可能已过期,此设备应用在收到通知时应显示提示用户启动应用的通用通知。If your application does not store authentication tokens on the device, or if these tokens can be expired, the device app, upon receiving the notification should display a generic notification prompting the user to launch the app. 然后,应用对用户进行身份验证并显示通知有效负载。The app then authenticates the user and shows the notification payload.

本安全推送教程演示如何安全地发送推送通知。This Secure Push tutorial shows how to send a push notification securely. 本教程以“通知用户”教程为基础,因此应先完成该教程中的步骤。The tutorial builds on the Notify Users tutorial, so you should complete the steps in that tutorial first.


本教程假设已根据通知中心入门 (iOS) 中所述创建并配置了通知中心。This tutorial assumes that you have created and configured your notification hub as described in Getting Started with Notification Hubs (iOS).

WebAPI 项目WebAPI Project

  1. 在 Visual Studio 中,打开在通知用户教程中创建的 AppBackend 项目。In Visual Studio, open the AppBackend project that you created in the Notify Users tutorial.

  2. 在 Notifications.cs 中,将整个 Notifications 类替换为以下代码 。In Notifications.cs, replace the whole Notifications class with the following code. 请确保将占位符替换为通知中心的连接字符串(具有完全访问权限)和中心名称。Be sure to replace the placeholders with your connection string (with full access) for your notification hub, and the hub name. 可以从 Azure 门户获取这些值。You can obtain these values from the Azure portal. 现在,该模块表示要发送的其他安全通知。This module now represents the different secure notifications that will be sent. 在完整的实现中,通知将存储在数据库中;为简单起见,在此示例中我们将它们存储在内存中。In a complete implementation, the notifications will be stored in a database; for simplicity, in this case we store them in memory.

     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
             return notification;
         public Notification ReadNotification(int id)
             return notifications.ElementAt(id);
  3. 在 NotificationsController.cs 中,将 NotificationsController 类定义中的代码替换为以下代码 。In NotificationsController.cs, replace the code inside the NotificationsController class definition with the following code. 该组件为设备实现了一种安全检索通知的方法,还提供了一种方法来触发到设备的安全推送(用于本教程的教学目的)。This component implements a way for the device to retrieve the notification securely, and also provides a way (for the purposes of this tutorial) to trigger a secure push to your devices. 请注意,在向通知中心发送通知时,我们将只发送一个包含通知 ID(且没有实际的消息内容)的原始通知:Note that when sending the notification to the notification hub, we only send a raw notification with the ID of the notification (and no actual message):

     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 方法现在不发送 toast 通知。Note that the Post method now does not send a toast notification. 它将发送只包含通知 ID 且没有任何敏感内容的原始通知。It sends a raw notification that contains only the notification ID, and not any sensitive content. 另外,请确保注释针对某些平台(未在通知中心上配置其凭据)的发送操作,因为它们会导致错误。Also, make sure to comment the send operation for the platforms for which you do not have credentials configured on your notification hub, as they will result in errors.

  1. 现在,我们将此应用重新部署到 Azure 网站,以便可以从所有设备对其进行访问。Now we will re-deploy this app to an Azure Website in order to make it accessible from all devices. 右键单击 AppBackend 项目,然后选择“发布” 。Right-click on the AppBackend project and select Publish.
  2. 选择 Azure 网站作为发布目标。Select Azure Website as your publish target. 使用 Azure 帐户登录,选择现有网站或新网站,并记下“连接” 选项卡中的“目标 URL” 属性。在本教程后面的部分中,我们将此 URL 称为后端终结点Sign in with your Azure account and select an existing or new Website, and make a note of the destination URL property in the Connection tab. We will refer to this URL as your backend endpoint later in this tutorial. 单击“发布”。 Click Publish.

修改 iOS 项目Modify the iOS project

现在,将应用后端修改为只发送通知的 ID,必须更改 iOS 应用来处理该通知并回调后端以检索要显示的安全消息 。Now that you modified your app back-end to send just the ID of a notification, you have to change your iOS app to handle that notification and call back your back-end to retrieve the secure message to be displayed.

若要实现此目标,我们必须编写逻辑来从应用后端检索安全内容。To achieve this goal, we have to write the logic to retrieve the secure content from the app back-end.

  1. AppDelegate.m 中,请确保该应用将注册无提示通知,以便它可以处理从后端发送的通知 ID。In AppDelegate.m, make sure the app registers for silent notifications so it processes the notification ID sent from the backend. 在 didFinishLaunchingWithOptions 中添加 UIRemoteNotificationTypeNewsstandContentAvailability 选项:Add the UIRemoteNotificationTypeNewsstandContentAvailability option in didFinishLaunchingWithOptions:

    [[UIApplication sharedApplication] registerForRemoteNotificationTypes: UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeNewsstandContentAvailability];
  2. AppDelegate.m 中,通过以下声明在顶部添加实现部分:In your AppDelegate.m add an implementation section at the top with the following declaration:

    @interface AppDelegate ()
    - (void) retrieveSecurePayloadWithId:(int)payloadId completion: (void(^)(NSString*, NSError*)) completion;
  3. 然后将以下代码添加到实现部分,将占位符 {back-end endpoint} 替换为先前获取的后端终结点:Then add in the implementation section the following code, substituting the placeholder {back-end endpoint} with the endpoint for your back-end obtained previously:

    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]
        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);
                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];

    此方法使用存储在共享首选项中的凭据调用应用后端来检索通知内容。This method calls your app back-end to retrieve the notification content using the credentials stored in the shared preferences.

  4. 现在我们必须处理传入通知,并使用上面的方法来检索要显示的内容。Now we have to handle the incoming notification and use the method above to retrieve the content to display. 首先,我们必须使 iOS 应用能在接收推送通知时在后台运行。First, we have to enable your iOS app to run in the background when receiving a push notification. 在 XCode 中,在左侧面板上选择应用项目,然后单击中央窗格的“目标”部分中的主应用目标 。In XCode, select your app project on the left panel, then click your main app target in the Targets section from the central pane.

  5. 然后,单击中央窗格顶部的“功能”选项卡,然后选中“远程通知”复选框 。Then click your Capabilities tab at the top of your central pane, and check the Remote Notifications checkbox.

  6. AppDelegate.m 中,添加以下方法来处理推送通知:In AppDelegate.m add the following method to handle push notifications:

    -(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];
            } else {

    请注意,最好由后端处理缺失身份验证标头属性或拒绝的情况。Note that it is preferable to handle the cases of missing authentication header property or rejection by the back-end. 这些情况下的特定处理主要取决于目标用户的体验。The specific handling of these cases depends mostly on your target user experience. 一种选择是显示包含用户用来进行身份验证的通用提示的通知,从而检索实际通知。One option is to display a notification with a generic prompt for the user to authenticate to retrieve the actual notification.

运行应用程序Run the Application

若要运行应用程序,请执行以下操作:To run the application, do the following:

  1. 在 XCode 中,在物理 iOS 设备上运行此应用(推送通知将无法在模拟器中正常工作)。In XCode, run the app on a physical iOS device (push notifications will not work in the simulator).
  2. 在 iOS 应用 UI 中,输入用户名和密码。In the iOS app UI, enter a username and password. 这些信息可以是任意字符串,但必须是相同的值。These can be any string, but they must be the same value.
  3. 在 iOS 应用 UI 中,单击“登录” 。In the iOS app UI, click Log in. 然后单击“发送推送” 。Then click Send push. 应该能看到通知中心中所显示的安全通知。You should see the secure notification being displayed in your notification center.