向 Windows 应用添加身份验证
概述
本主题演示如何向移动应用添加基于云的身份验证。 在本教程中,使用 Azure 应用服务支持的标识提供者向移动应用的通用 Windows 平台 (UWP) 快速入门项目添加身份验证。 在移动应用后端成功进行身份验证和授权后,显示用户 ID 值。
本教程基于移动应用快速入门。 必须先完成移动应用入门教程。
注册应用以进行身份验证并配置应用服务
首先,需要在标识提供者站点上注册应用,然后在移动应用后端设置提供者生成的凭据。
请按照以下提供者特定的说明来配置首选标识提供者:
为要在应用中支持的各提供者重复上述步骤。
将应用添加到允许的外部重定向 URL
安全身份验证要求为应用定义新的 URL 方案。 这允许身份验证系统在身份验证过程完成后,重定向回应用。 在本教程中,我们将通篇使用 URL 方案 appname。 但是,可以使用所选择的任何 URL 方案。 对于移动应用程序而言,它应是唯一的。 在服务器端启用重定向:
在 Azure 门户中,选择应用服务。
单击“身份验证/授权”菜单选项。
在“允许的外部重定向 URL”中,输入
url_scheme_of_your_app://easyauth.callback
。 此字符串中的 url_scheme_of_your_app 是移动应用程序的 URL 方案。 它应该遵循协议的正常 URL 规范(仅使用字母和数字,并以字母开头)。 请记下所选的字符串,你将需要在几个地方使用 URL 方案调整移动应用程序代码。单击“保存” 。
将权限限制给已经过身份验证的用户
默认情况下,可匿名调用移动应用后端中的 API。 接下来,需限制为仅可访问已验证的客户端。
Node.js 后端(通过 Azure 门户):
在移动应用设置中,单击“简易表”并选择相应的表。 单击“更改权限”,为所有权限选择“仅限已验证的访问”,并单击“保存”。
.NET 后端 (C#):
在服务器项目中,导航到“控制器”>“TodoItemController.cs”。 将
[Authorize]
属性添加到“TodoItemController”类,如下所示。 若要限制为仅可访问特定方法,还可只向这些方法应用此属性(而非类)。 重新发布服务器项目。[Authorize] public class TodoItemController : TableController<TodoItem>
Node.js 后端(通过 Node.js 代码):
若要访问表时需验证身份,请向 Node.js 服务器脚本添加以下行:
table.access = 'authenticated';
有关更多详细信息,请参阅如何:要求在访问表时进行身份验证。 若要了解如何从网站下载快速入门代码项目,请参阅如何:使用 Git 下载 Node.js 后端快速入门代码项目。
现在,可以验证是否已禁用对后端的匿名访问。 将 UWP 应用项目设为启动项目后,部署并运行该应用;验证启动该应用后,是否会引发状态代码为 401(“未授权”)的未处理异常。 发生此异常的原因是应用尝试以未经身份验证的用户身份访问移动应用代码,但 TodoItem 表现在要求身份验证。
接下来,更新应用,以便在从应用服务请求资源之前对用户进行身份验证。
向应用程序添加身份验证
在 UWP 应用项目文件 MainPage.xaml.cs 中,添加以下代码片段:
// Define a member variable for storing the signed-in user. private MobileServiceUser user; // Define a method that performs the authentication process // using a Facebook 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 Facebook authentication. user = await App.MobileService .LoginAsync(MobileServiceAuthenticationProvider.Facebook, "{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; }
此代码使用 Facebook 登录对用户进行身份验证。 如果使用的标识提供商不是 Facebook,请将上述 MobileServiceAuthenticationProvider 的值更改为提供商的值。
替换 MainPage.xaml.cs 中的 OnNavigatedTo() 方法。 接下来,向应用添加用于触发身份验证的“登录”按钮。
protected override async void OnNavigatedTo(NavigationEventArgs e) { if (e.Parameter is Uri) { App.MobileService.ResumeWithURL(e.Parameter as Uri); } }
将以下代码片段添加到 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(); } }
打开 MainPage.xaml 项目文件,找到定义“保存”按钮的元素,将其替换为以下代码:
<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>
将以下代码片段添加到 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); }
打开 Package.appxmanifest 文件,导航到“声明”,在“可用声明”下拉列表中,选择“协议”,然后单击“添加”按钮。 现在,配置“协议”声明的“属性”。 在“显示名称”中,添加要向应用程序用户显示的名称。 在“名称”中,添加 {url_scheme_of_your_app}。
按 F5 键运行该应用,单击“登录”按钮,并使用所选的标识提供者登录到该应用。 成功登录后,该应用运行时不会出错,用户能够查询后端,并对数据进行更新。
在客户端上存储身份验证令牌
前一示例显示了标准登录,这要求在该应用每次启动时客户端同时联系标识提供者和应用服务。 此方法不仅效率低下,而且如果很多客户尝试同时启动应用,会遇到关于使用率的问题。 更好的方法是缓存应用服务返回的授权令牌,并在使用基于提供者的登录之前首先尝试使用此令牌。
注意
无论使用的是客户端管理的还是服务管理的身份验证,都可以缓存应用服务颁发的令牌。 本教程使用服务管理的身份验证。
在 MainPage.xaml.cs 项目文件中,添加以下 using 语句:
using System.Linq; using Windows.Security.Credentials;
将 AuthenticateAsync 方法替换为以下代码:
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: https://aka.ms/jww5vp. success = true; message = string.Format("Cached credentials for user - {0}", user.UserId); } else { try { // Sign in with the identity provider. user = await App.MobileService .LoginAsync(provider, "{url_scheme_of_your_app}"); // 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 signed in - {0}", user.UserId); } catch (MobileServiceInvalidOperationException) { message = "You must sign in. Sign-In Required"; } } var dialog = new MessageDialog(message); dialog.Commands.Add(new UICommand("OK")); await dialog.ShowAsync(); return success; }
在此版本的 AuthenticateAsync 中,应用将尝试使用存储在 PasswordVault 中的凭据来访问服务。 没有存储任何凭证时,也执行常规登录。
注意
缓存的令牌可能已过期,正在使用应用时,在身份验证之后也可能会发生令牌到期。 若要了解如何确定令牌是否已过期,请参阅检查过期的身份验证令牌。 有关用于处理到期令牌相关的授权错误的解决方案,请参阅文章在 Azure 移动服务托管 SDK 中缓存和处理到期令牌。
两次重新启动此应用。
请注意,在第一次启动时,再次需要使用此提供商进行登录。 但是,在第二次重新启动时,将使用缓存的凭证,而绕过登录。
后续步骤
完成此基本身份验证教程后,请考虑继续学习以下教程之一: