从 Azure 通知中心安全地推送通知Securely push notifications from Azure Notification Hubs

概述Overview

利用 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 completely 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.

备注

本教程假设你已根据通知中心入门(Windows 应用商店)中所述创建并配置了通知中心。This tutorial assumes that you have created and configured your notification hub as described in Getting Started with Notification Hubs (Windows Store). 此外,请注意 Windows Phone 8.1 需要 Windows(而不是 Windows Phone)凭据,且后台任务无法在 Windows Phone 8.0 或 Silverlight 8.1 上正常运行。Also, note that Windows Phone 8.1 requires Windows (not Windows Phone) credentials, and that background tasks do not work on Windows Phone 8.0 or Silverlight 8.1. 对于 Windows 应用商店应用程序,您只能在应用锁屏界面启用(单击 Appmanifest 中的复选框)的情况下,通过运行后台任务来接收通知。For Windows Store applications, you can receive notifications via a background task only if the app is lock-screen enabled (click the checkbox in the Appmanifest).

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
             };
    
             notifications.Add(notification);
    
             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.

修改 Windows Phone 项目Modify the Windows Phone Project

  1. NotifyUserWindowsPhone 项目中,将以下代码添加到 App.xaml.cs 注册推送后台任务。In the NotifyUserWindowsPhone project, add the following code to App.xaml.cs to register the push background task. OnLaunched() 方法的末尾添加以下代码行:Add the following line of code at the end of the OnLaunched() method:

    RegisterBackgroundTask();
    
  2. 仍在 App.xaml.cs 中,紧跟 OnLaunched() 方法添加以下代码:Still in App.xaml.cs, add the following code immediately after the OnLaunched() method:

    private async void RegisterBackgroundTask()
    {
        if (!Windows.ApplicationModel.Background.BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name == "PushBackgroundTask"))
        {
            var result = await BackgroundExecutionManager.RequestAccessAsync();
            var builder = new BackgroundTaskBuilder();
    
            builder.Name = "PushBackgroundTask";
            builder.TaskEntryPoint = typeof(PushBackgroundComponent.PushBackgroundTask).FullName;
            builder.SetTrigger(new Windows.ApplicationModel.Background.PushNotificationTrigger());
            BackgroundTaskRegistration task = builder.Register();
        }
    }
    
  3. 在 App.xaml.cs 文件的顶部添加以下 using 语句:Add the following using statements at the top of the App.xaml.cs file:

    using Windows.Networking.PushNotifications;
    using Windows.ApplicationModel.Background;
    
  4. 从 Visual Studio 的“文件”菜单中,单击“全部保存” 。From the File menu in Visual Studio, click Save All.

创建推送背景组件Create the Push Background Component

下一步是创建推送背景组件。The next step is to create the push background component.

  1. 在“解决方案资源管理器”中,右键单击解决方案的顶层节点(在本例中为 Solution SecurePush),然后依次单击“添加”和“新建项目” 。In Solution Explorer, right-click the top-level node of the solution (Solution SecurePush in this case), then click Add, then click New Project.

  2. 展开“应用商店应用”,然后依次单击“Windows Phone 应用”和“Windows 运行时组件 (Windows Phone)” 。Expand Store Apps, then click Windows Phone Apps, then click Windows Runtime Component (Windows Phone). 将该项目命名为 PushBackgroundComponent,然后单击“确定”创建项目 。Name the project PushBackgroundComponent, and then click OK to create the project.

  3. 在“解决方案资源管理器”中,右键单击“PushBackgroundComponent (Windows Phone 8.1)”项目,然后依次单击“添加”和“类” 。In Solution Explorer, right-click the PushBackgroundComponent (Windows Phone 8.1) project, then click Add, then click Class. 将新类命名为 PushBackgroundTask.csName the new class PushBackgroundTask.cs. 单击“添加”生成类 。Click Add to generate the class.

  4. PushBackgroundComponent 命名空间定义的整个内容替换为以下代码,将占位符 {back-end endpoint} 替换为部署后端时获取的后端终结点:Replace the entire contents of the PushBackgroundComponent namespace definition with the following code, substituting the placeholder {back-end endpoint} with the back-end endpoint obtained while deploying your back-end:

    public sealed class Notification
        {
            public int Id { get; set; }
            public string Payload { get; set; }
            public bool Read { get; set; }
        }
    
        public sealed class PushBackgroundTask : IBackgroundTask
        {
            private string GET_URL = "{back-end endpoint}/api/notifications/";
    
            async void IBackgroundTask.Run(IBackgroundTaskInstance taskInstance)
            {
                // Store the content received from the notification so it can be retrieved from the UI.
                RawNotification raw = (RawNotification)taskInstance.TriggerDetails;
                var notificationId = raw.Content;
    
                // retrieve content
                BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
                var httpClient = new HttpClient();
                var settings = ApplicationData.Current.LocalSettings.Values;
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", (string)settings["AuthenticationToken"]);
    
                var notificationString = await httpClient.GetStringAsync(GET_URL + notificationId);
    
                var notification = JsonConvert.DeserializeObject<Notification>(notificationString);
    
                ShowToast(notification);
    
                deferral.Complete();
            }
    
            private void ShowToast(Notification notification)
            {
                ToastTemplateType toastTemplate = ToastTemplateType.ToastText01;
                XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);
                XmlNodeList toastTextElements = toastXml.GetElementsByTagName("text");
                toastTextElements[0].AppendChild(toastXml.CreateTextNode(notification.Payload));
                ToastNotification toast = new ToastNotification(toastXml);
                ToastNotificationManager.CreateToastNotifier().Show(toast);
            }
        }
    
  5. 在“解决方案资源管理器”中,右键单击“PushBackgroundComponent (Windows Phone 8.1)”项目,然后单击“管理 NuGet 包” 。In Solution Explorer, right-click the PushBackgroundComponent (Windows Phone 8.1) project and then click Manage NuGet Packages.

  6. 在左侧单击“联机” 。On the left-hand side, click Online.

  7. 在“搜索”框中键入 Http 客户端 。In the Search box, type Http Client.

  8. 在结果列表中,单击“Microsoft HTTP 客户端库”,然后单击“安装” 。In the results list, click Microsoft HTTP Client Libraries, and then click Install. 完成安装。Complete the installation.

  9. 返回到 NuGet“搜索”框,键入 Json.net 。Back in the NuGet Search box, type Json.net. 安装 Json.NET 包,并关闭“NuGet 包管理器”窗口。Install the Json.NET package, then close the NuGet Package Manager window.

  10. PushBackgroundTask.cs 文件的顶部,添加以下 using 语句:Add the following using statements at the top of the PushBackgroundTask.cs file:

    using Windows.ApplicationModel.Background;
    using Windows.Networking.PushNotifications;
    using System.Net.Http;
    using Windows.Storage;
    using System.Net.Http.Headers;
    using Newtonsoft.Json;
    using Windows.UI.Notifications;
    using Windows.Data.Xml.Dom;
    
  11. 在“解决方案资源管理器”的 NotifyUserWindowsPhone (Windows Phone 8.1) 项目中,右键单击“引用”,然后单击“添加引用...” 。在“引用管理器”对话框中,选中 PushBackgroundComponent 旁边的复选框,然后单击“确定” 。In Solution Explorer, in the NotifyUserWindowsPhone (Windows Phone 8.1) project, right-click References, then click Add Reference.... In the Reference Manager dialog, check the box next to PushBackgroundComponent, and then click OK.

  12. 在“解决方案资源管理器”中,双击 NotifyUserWindowsPhone (Windows Phone 8.1) 项目中的“Package.appxmanifest” 。In Solution Explorer, double-click Package.appxmanifest in the NotifyUserWindowsPhone (Windows Phone 8.1) project. 在“通知”下,将“Toast Capable”设置为“是” 。Under Notifications, set Toast Capable to Yes.

  13. 仍在 Package.appxmanifest 中,单击顶部附近的“声明”菜单 。Still in Package.appxmanifest, click the Declarations menu near the top. 在“可用声明”下拉列表中,单击“后台任务”,然后单击“添加” 。In the Available Declarations dropdown, click Background Tasks, and then click Add.

  14. 在“属性”下的 Package.appxmanifest 中选中“推送通知” 。In Package.appxmanifest, under Properties, check Push notification.

  15. 在“应用设置”下的 Package.appxmanifest 中,在“入口点”字段中键入 PushBackgroundComponent.PushBackgroundTask 。In Package.appxmanifest, under App Settings, type PushBackgroundComponent.PushBackgroundTask in the Entry Point field.

  16. 在“文件”菜单中,单击“全部保存” 。From the File menu, click Save All.

运行应用程序Run the Application

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

  1. 在 Visual Studio 中运行此 AppBackend Web API 应用程序。In Visual Studio, run the AppBackend Web API application. 显示 ASP.NET 网页。An ASP.NET web page is displayed.
  2. 在 Visual Studio 中运行此 NotifyUserWindowsPhone (Windows Phone 8.1) Windows Phone 应用 。In Visual Studio, run the NotifyUserWindowsPhone (Windows Phone 8.1) Windows Phone app. Windows Phone 模拟器自动运行并加载应用程序。The Windows Phone emulator runs and loads the app automatically.
  3. NotifyUserWindowsPhone 应用 UI 中,输入用户名和密码。In the NotifyUserWindowsPhone app UI, enter a username and password. 这些信息可以是任意字符串,但必须是相同的值。These can be any string, but they must be the same value.
  4. 在 NotifyUserWindowsPhone 应用 UI 中,单击“登录并注册” 。In the NotifyUserWindowsPhone app UI, click Log in and register. 然后单击“发送推送” 。Then click Send push.