向 Windows 应用添加身份验证Add authentication to your Windows app

Note

Visual Studio App Center 正在投资于对移动应用开发至关重要的新集成服务。Visual Studio App Center is investing in new and integrated services central to mobile app development. 开发人员可以使用生成测试分发服务来设置持续集成和交付管道。Developers can use Build, Test and Distribute services to set up Continuous Integration and Delivery pipeline. 部署应用后,开发人员可以使用分析诊断服务监视其应用的状态和使用情况,并使用推送服务与用户互动。Once the app is deployed, developers can monitor the status and usage of their app using the Analytics and Diagnostics services, and engage with users using the Push service. 开发人员还可以利用 Auth 对用户进行身份验证,利用数据服务在云中持久保存和同步应用数据。Developers can also leverage Auth to authenticate their users and Data service to persist and sync app data in the cloud. 立即查看 App CenterCheck out App Center today.

概述Overview

本主题演示如何向移动应用添加基于云的身份验证。This topic shows you how to add cloud-based authentication to your mobile app. 在本教程中,使用 Azure 应用服务支持的标识提供者向移动应用的通用 Windows 平台 (UWP) 快速入门项目添加身份验证。In this tutorial, you add authentication to the Universal Windows Platform (UWP) quickstart project for Mobile Apps using an identity provider that is supported by Azure App Service. 在移动应用后端成功进行身份验证和授权后,显示用户 ID 值。After being successfully authenticated and authorized by your Mobile App backend, the user ID value is displayed.

本教程基于移动应用快速入门。This tutorial is based on the Mobile Apps quickstart. 必须先完成移动应用入门教程。You must first complete the tutorial Get started with Mobile Apps.

注册应用以进行身份验证并配置应用服务Register your app for authentication and configure the App Service

首先,需要在标识提供者站点上注册应用,然后在移动应用后端设置提供者生成的凭据。First, you need to register your app at an identity provider's site, and then you will set the provider-generated credentials in the Mobile Apps back end.

  1. 请按照以下提供者特定的说明来配置首选标识提供者:Configure your preferred identity provider by following the provider-specific instructions:

  2. 为要在应用中支持的各提供者重复上述步骤。Repeat the previous steps for each provider you want to support in your app.

将应用添加到允许的外部重定向 URLAdd your app to the Allowed External Redirect URLs

安全身份验证要求为应用定义新的 URL 方案。Secure authentication requires that you define a new URL scheme for your app. 此方案允许在完成身份验证过程后,身份验证系统重定向到应用。This allows the authentication system to redirect back to your app once the authentication process is complete. 在本教程中,我们自始至终使用 URL 方案 appnameIn this tutorial, we use the URL scheme appname throughout. 但是,可以使用任何你所选的 URL 方案。However, you can use any URL scheme you choose. 对于移动应用程序而言,它应是唯一的。It should be unique to your mobile application. 在服务器端启用重定向:To enable the redirection on the server side:

  1. Azure 门户中,选择应用服务。In the Azure portal, select your App Service.

  2. 单击“身份验证/授权” 菜单选项。Click the Authentication / Authorization menu option.

  3. 在“允许的外部重定向 URL” 中,输入 url_scheme_of_your_app://easyauth.callbackIn the Allowed External Redirect URLs, enter url_scheme_of_your_app://easyauth.callback. 此字符串中的 url_scheme_of_your_app 是移动应用程序的 URL 方案。The url_scheme_of_your_app in this string is the URL Scheme for your mobile application. 它应该遵循协议的正常 URL 规范(仅使用字母和数字,并以字母开头)。It should follow normal URL specification for a protocol (use letters and numbers only, and start with a letter). 应记下此字符串,因为在一些地方需要使用此 URL 方案调整移动应用代码。You should make a note of the string that you choose as you will need to adjust your mobile application code with the URL Scheme in several places.

  4. 单击“保存” 。Click Save.

将权限限制给已经过身份验证的用户Restrict permissions to authenticated users

默认情况下,可匿名调用移动应用后端中的 API。By default, APIs in a Mobile Apps back end can be invoked anonymously. 接下来,需限制为仅可访问已验证的客户端。Next, you need to restrict access to only authenticated clients.

  • Node.js 后端(通过 Azure 门户)Node.js back end (via the Azure portal) :

    在移动应用设置中,单击“简易表” 并选择相应的表。In your Mobile Apps settings, click Easy Tables and select your table. 单击“更改权限” ,为所有权限选择“仅限已验证的访问” ,并单击“保存” 。Click Change permissions, select Authenticated access only for all permissions, and then click Save.

  • .NET 后端 (C#) :.NET back end (C#):

    在服务器项目中,导航到“控制器” > “TodoItemController.cs”。 In the server project, navigate to Controllers > TodoItemController.cs. [Authorize] 属性添加到“TodoItemController”类,如下所示。 Add the [Authorize] attribute to the TodoItemController class, as follows. 若要限制为仅可访问特定方法,还可只向这些方法应用此属性(而非类)。To restrict access only to specific methods, you can also apply this attribute just to those methods instead of the class. 重新发布服务器项目。Republish the server project.

    [Authorize]
    public class TodoItemController : TableController<TodoItem>
    
  • Node.js 后端(通过 Node.js 代码)Node.js backend (via Node.js code) :

    若要访问表时需验证身份,请向 Node.js 服务器脚本添加以下行:To require authentication for table access, add the following line to the Node.js server script:

    table.access = 'authenticated';
    

    有关更多详细信息,请参阅如何:要求在访问表时进行身份验证For more details, see How to: Require authentication for access to tables. 若要了解如何从站点下载快速入门代码项目,请参阅如何:使用 Git 下载 Node.js 后端快速入门代码项目To learn how to download the quickstart code project from your site, see How to: Download the Node.js backend quickstart code project using Git.

现在,可以验证是否已禁用对后端的匿名访问。Now, you can verify that anonymous access to your backend has been disabled. 将 UWP 应用项目设为启动项目后,部署并运行该应用;验证启动该应用后,是否会引发状态代码为 401(“未授权”)的未处理异常。With the UWP app project set as the start-up project, deploy and run the app; verify that an unhandled exception with a status code of 401 (Unauthorized) is raised after the app starts. 发生此异常的原因是应用尝试以未经身份验证的用户身份访问移动应用代码,但 TodoItem 表现在要求身份验证。This happens because the app attempts to access your Mobile App Code as an unauthenticated user, but the TodoItem table now requires authentication.

接下来,更新应用,以便在从应用服务请求资源之前对用户进行身份验证。Next, you will update the app to authenticate users before requesting resources from your App Service.

向应用程序添加身份验证Add authentication to the app

  1. 在 UWP 应用项目文件 MainPage.xaml.cs 中,添加以下代码片段:In the UWP app project file MainPage.xaml.cs and add the following code snippet:

    // Define a member variable for storing the signed-in user. 
    private MobileServiceUser user;
    
    // Define a method that performs the authentication process
    // using a Microsoft sign-in. 
    private async System.Threading.Tasks.Task<bool> AuthenticateAsync()
    {
        string message;
        bool success = false;
        try
        {
            // Change 'MobileService' to the name of your MobileServiceClient instance.
            // Sign-in using MicrosoftAccount authentication.
            user = await App.MobileService
                    .LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount, "{url_scheme_of_your_app}");
            message =
                string.Format("You are now signed in - {0}", user.UserId);
    
            success = true;
        }
        catch (InvalidOperationException)
        {
            message = "You must log in. Login Required";
        }
    
        var dialog = new MessageDialog(message);
        dialog.Commands.Add(new UICommand("OK"));
        await dialog.ShowAsync();
        return success;
    }
    

    此代码使用 MicrosoftAccount 登录对用户进行身份验证。This code authenticates the user with a MicrosoftAccount login. 如果使用的标识提供者不是 MicrosoftAccount,请将上述 MobileServiceAuthenticationProvider 的值更改为提供者的值。If you are using an identity provider other than MicrosoftAccount, change the value of MobileServiceAuthenticationProvider above to the value for your provider.

  2. 替换 MainPage.xaml.cs 中的 OnNavigatedTo() 方法。Replace the OnNavigatedTo() method in MainPage.xaml.cs. 接下来,向应用添加用于触发身份验证的“登录” 按钮。Next, you will add a Sign in button to the app that triggers authentication.

     protected override async void OnNavigatedTo(NavigationEventArgs e)
     {
         if (e.Parameter is Uri)
         {
             App.MobileService.ResumeWithURL(e.Parameter as Uri);
         }
     }
    
  3. 将以下代码片段添加到 MainPage.xaml.cs:Add the following code snippet to the MainPage.xaml.cs:

    private async void ButtonLogin_Click(object sender, RoutedEventArgs e)
    {
        // Login the user and then load data from the mobile app.
        if (await AuthenticateAsync())
        {
            // Switch the buttons and load items from the mobile app.
            ButtonLogin.Visibility = Visibility.Collapsed;
            ButtonSave.Visibility = Visibility.Visible;
            //await InitLocalStoreAsync(); //offline sync support.
            await RefreshTodoItems();
        }
    }
    
  4. 打开 MainPage.xaml 项目文件,找到定义“保存” 按钮的元素,将其替换为以下代码:Open the MainPage.xaml project file, locate the element that defines the Save button and replace it with the following code:

    <Button Name="ButtonSave" Visibility="Collapsed" Margin="0,8,8,0" 
            Click="ButtonSave_Click">
        <StackPanel Orientation="Horizontal">
            <SymbolIcon Symbol="Add"/>
            <TextBlock Margin="5">Save</TextBlock>
        </StackPanel>
    </Button>
    <Button Name="ButtonLogin" Visibility="Visible" Margin="0,8,8,0" 
            Click="ButtonLogin_Click" TabIndex="0">
        <StackPanel Orientation="Horizontal">
            <SymbolIcon Symbol="Permissions"/>
            <TextBlock Margin="5">Sign in</TextBlock> 
        </StackPanel>
    </Button>
    
  5. 将以下代码片段添加到 App.xaml.cs:Add the following code snippet to the App.xaml.cs:

     protected override void OnActivated(IActivatedEventArgs args)
     {
         if (args.Kind == ActivationKind.Protocol)
         {
             ProtocolActivatedEventArgs protocolArgs = args as ProtocolActivatedEventArgs;
             Frame content = Window.Current.Content as Frame;
             if (content.Content.GetType() == typeof(MainPage))
             {
                 content.Navigate(typeof(MainPage), protocolArgs.Uri);
             }
         }
         Window.Current.Activate();
         base.OnActivated(args);
     }
    
  6. 打开 Package.appxmanifest 文件,导航到“声明” ,在“可用声明” 下拉列表中,选择“协议” 并单击“添加” 按钮。Open Package.appxmanifest file, navigate to Declarations, in Available Declarations dropdown list, select Protocol and click Add button. 现在,配置协议声明的属性Now configure the Properties of the Protocol declaration. 在“显示名称” 中,添加要向应用程序的用户显示的名称。In Display name, add the name you wish to display to users of your application. 在“名称” 中,添加 {url_scheme_of_your_app}。In Name, add your {url_scheme_of_your_app}.

  7. 按 F5 键运行该应用,单击“登录” 按钮,然后使用所选的标识提供者登录到该应用。Press the F5 key to run the app, click the Sign in button, and sign into the app with your chosen identity provider. 成功登录后,该应用运行时不会出错,用户能够查询后端,并对数据进行更新。After your sign-in is successful, the app runs without errors and you are able to query your backend and make updates to data.

在客户端上存储身份验证令牌Store the authentication token on the client

前一示例显示了标准登录,这要求在该应用每次启动时客户端同时联系标识提供者和应用服务。The previous example showed a standard sign-in, which requires the client to contact both the identity provider and the App Service every time that the app starts. 此方法不仅效率低下,而且如果很多客户尝试同时启动应用,会遇到关于使用率的问题。Not only is this method inefficient, you can run into usage-relates issues should many customers try to start you app at the same time. 更好的方法是缓存应用服务返回的授权令牌,并在使用基于提供者的登录之前首先尝试使用此令牌。A better approach is to cache the authorization token returned by your App Service and try to use this first before using a provider-based sign-in.

Note

无论使用的是客户端管理的还是服务管理的身份验证,都可以缓存应用服务颁发的令牌。You can cache the token issued by App Services regardless of whether you are using client-managed or service-managed authentication. 本教程使用服务管理的身份验证。This tutorial uses service-managed authentication.

  1. 在 MainPage.xaml.cs 项目文件中,添加以下 using 语句:In the MainPage.xaml.cs project file, add the following using statements:

     using System.Linq;        
     using Windows.Security.Credentials;
    
  2. AuthenticateAsync 方法替换为以下代码:Replace the AuthenticateAsync method with the following code:

     private async System.Threading.Tasks.Task<bool> AuthenticateAsync()
     {
         string message;
         bool success = false;
    
         // This sample uses the Facebook provider.
         var provider = MobileServiceAuthenticationProvider.Facebook;
    
         // Use the PasswordVault to securely store and access credentials.
         PasswordVault vault = new PasswordVault();
         PasswordCredential credential = null;
    
         try
         {
             // Try to get an existing credential from the vault.
             credential = vault.FindAllByResource(provider.ToString()).FirstOrDefault();
         }
         catch (Exception)
         {
             // When there is no matching resource an error occurs, which we ignore.
         }
    
         if (credential != null)
         {
             // Create a user from the stored credentials.
             user = new MobileServiceUser(credential.UserName);
             credential.RetrievePassword();
             user.MobileServiceAuthenticationToken = credential.Password;
    
             // Set the user from the stored credentials.
             App.MobileService.CurrentUser = user;
    
             // Consider adding a check to determine if the token is 
             // expired, as shown in this post: http://aka.ms/jww5vp.
    
             success = true;
             message = string.Format("Cached credentials for user - {0}", user.UserId);
         }
         else
         {
             try
             {
                 // Login with the identity provider.
                 user = await App.MobileService
                     .LoginAsync(provider);
    
                 // Create and store the user credentials.
                 credential = new PasswordCredential(provider.ToString(),
                     user.UserId, user.MobileServiceAuthenticationToken);
                 vault.Add(credential);
    
                 success = true;
                 message = string.Format("You are now logged in - {0}", user.UserId);
             }
             catch (MobileServiceInvalidOperationException)
             {
                 message = "You must log in. Login Required";
             }
         }
    
         var dialog = new MessageDialog(message);
         dialog.Commands.Add(new UICommand("OK"));
         await dialog.ShowAsync();
    
         return success;
     }
    

    在此版本的 AuthenticateAsync 中,应用将尝试使用存储在 PasswordVault 中的凭据来访问服务。In this version of AuthenticateAsync, the app tries to use credentials stored in the PasswordVault to access the service. 没有存储任何凭据时,也执行常规登录。A regular sign-in is also performed when there is no stored credential.

    Note

    缓存的令牌可能已过期,正在使用应用时,在身份验证之后也可能会发生令牌到期。A cached token may be expired, and token expiration can also occur after authentication when the app is in use. 若要了解如何确定令牌是否已过期,请参阅检查过期的身份验证令牌To learn how to determine if a token is expired, see Check for expired authentication tokens. 有关用于处理到期令牌相关的授权错误的解决方案,请参阅文章在 Azure 移动服务托管 SDK 中缓存和处理到期令牌For a solution to handling authorization errors related to expiring tokens, see the post Caching and handling expired tokens in Azure Mobile Services managed SDK.

  3. 两次重新启动此应用。Restart the app twice.

    请注意,在第一次启动时,再次需要使用此提供程序进行登录。Notice that on the first start-up, sign-in with the provider is again required. 但是,在第二次重新启动时,将使用缓存的凭据,而绕过登录。However, on the second restart the cached credentials are used and sign-in is bypassed.

后续步骤Next steps

完成此基本身份验证教程后,请考虑继续学习以下教程之一:Now that you completed this basic authentication tutorial, consider continuing on to one of the following tutorials:

  • 向应用添加推送通知Add push notifications to your app
    了解如何为应用添加推送通知支持,以及如何将移动应用后端配置为使用 Azure 通知中心发送推送通知。Learn how to add push notifications support to your app and configure your Mobile App backend to use Azure Notification Hubs to send push notifications.
  • 为应用启用脱机同步Enable offline sync for your app
    了解如何使用移动应用后端向应用添加脱机支持。Learn how to add offline support your app using an Mobile App backend. 借助脱机同步,最终用户即使在没有网络连接时也能够与移动应用进行交互(查看、添加或修改数据)。Offline sync allows end-users to interact with a mobile app—viewing, adding, or modifying data—even when there is no network connection.