注意
此产品已停用。 有关使用 .NET 8 或更高版本的项目的替换,请参阅 Community Toolkit Datasync 库。
在本教程中,将使用 Microsoft Entra ID 向 TodoApp 项目添加Microsoft身份验证。 在完成本教程之前,请确保已 创建项目并部署后端。
提示
尽管我们使用 Microsoft Entra ID 进行身份验证,但可以使用 Azure 移动应用所需的任何身份验证库。
将身份验证添加到后端服务
后端服务是标准 ASP.NET 6 服务。 演示如何为 ASP.NET 6 服务启用身份验证的任何教程都适用于 Azure 移动应用。
若要为后端服务启用Microsoft Entra 身份验证,需要:
- 使用 Microsoft Entra ID 注册应用程序。
- 向 ASP.NET 6 个后端项目添加身份验证检查。
注册应用程序
首先,在 Microsoft Entra 租户中注册 Web API,然后按照以下步骤添加范围:
- 登录 Azure 门户。 
- 如果有权访问多个租户,请使用顶部菜单中的 目录 + 订阅 筛选器切换到要在其中注册应用程序的租户。 
- 搜索并选择 Microsoft Entra ID。 
- 在“管理”下,选择“应用注册”“新建注册” 。 - 名称:输入应用程序的名称;例如,TodoApp 快速入门。 应用的用户将看到此名称。 稍后可以对其进行更改。
- 支持的帐户类型:任何组织目录中的帐户(任何Microsoft Entra 目录 - 多租户)和个人Microsoft帐户(例如 Skype、Xbox)
 
- 选择“注册”。 
- 在“管理”下,选择“公开 API>添加范围”。 
- 对于 应用程序 ID URI,请选择 保存并继续,以接受默认值。 
- 输入以下详细信息: - 
              范围名称:access_as_user
- 谁能同意? :管理员和用户
- 
              管理员许可显示名称:Access TodoApp
- 
              管理员许可说明:Allows the app to access TodoApp as the signed-in user.
- 
              用户同意显示名称:Access TodoApp
- 
              用户同意说明:Allow the app to access TodoApp on your behalf.
- 状态:已启用
 
- 
              范围名称:
- 选择“添加范围”以完成范围添加。 
- 请注意范围的值,类似于 - api://<client-id>/access_as_user(称为 Web API 范围)。 配置客户端时需要作用域。
- 选择“概述”。 
- 请注意 Essentials 部分中 应用程序(客户端)ID(称为 Web API 应用程序 ID)。 需要此值才能配置后端服务。 
打开 Visual Studio 并选择 TodoAppService.NET6 项目。
- 右键单击 - TodoAppService.NET6项目,然后选择 管理 NuGet 包...。
- 在新选项卡中,选择 浏览,然后在搜索框中输入 Microsoft.Identity.Web。   
- 选择 - Microsoft.Identity.Web包,然后按 安装。
- 按照提示完成包的安装。 
- 打开 - Program.cs。 将以下内容添加到- using语句列表:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
- 直接在调用 builder.Services.AddDbContext()上方添加以下代码:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddMicrosoftIdentityWebApi(builder.Configuration);
builder.Services.AddAuthorization();
- 直接在调用 app.MapControllers()上方添加以下代码:
app.UseAuthentication();
app.UseAuthorization();
现在,Program.cs 应如下所示:
using Microsoft.AspNetCore.Datasync;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
using TodoAppService.NET6.Db;
  
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
  
if (connectionString == null)
{
  throw new ApplicationException("DefaultConnection is not set");
}
  
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddMicrosoftIdentityWebApi(builder.Configuration);
builder.Services.AddAuthorization();
builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddDatasyncControllers();
  
var app = builder.Build();
  
// Initialize the database
using (var scope = app.Services.CreateScope())
{
  var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
  await context.InitializeDatabaseAsync().ConfigureAwait(false);
}
  
// Configure and run the web service.
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
- 编辑 Controllers\TodoItemController.cs。 向类添加[Authorize]属性。 你的类应如下所示:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Datasync;
using Microsoft.AspNetCore.Datasync.EFCore;
using Microsoft.AspNetCore.Mvc;
using TodoAppService.NET6.Db;
namespace TodoAppService.NET6.Controllers
{
  [Authorize]
  [Route("tables/todoitem")]
  public class TodoItemController : TableController<TodoItem>
  {
    public TodoItemController(AppDbContext context)
      : base(new EntityTableRepository<TodoItem>(context))
    {
    }
  }
}
- 编辑 appsettings.json。 添加以下块:
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com",
    "ClientId": "<client-id>",
    "TenantId": "common"
  },
将 <client-id> 替换为前面记录的 Web API 应用程序 ID。 完成后,它应如下所示:
{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com",
    "ClientId": "<client-id>",
    "TenantId": "common"
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=TodoApp;Trusted_Connection=True"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}
再次将服务发布到 Azure:
- 右键单击 TodoAppService.NET6项目,然后选择 发布...。
- 选择选项卡右上角的 “发布”按钮。
在浏览器中打开 https://yoursite.azurewebsites.net/tables/todoitem?ZUMO-API-VERSION=3.0.0。  请注意,该服务现在返回一个 401 响应,该响应指示需要身份验证。
               
              
            
将应用注册到标识服务
Microsoft数据同步框架内置支持使用 HTTP 事务标头中的 Json Web 令牌(JWT)的任何身份验证提供程序。 此应用程序使用 Microsoft身份验证库(MSAL) 请求此类令牌并授权已登录的用户访问后端服务。
配置本机客户端应用程序
可以使用客户端库(如Microsoft标识库(MSAL)注册本机客户端,以允许对应用中托管的 Web API 进行身份验证。
- 在 Azure 门户中,选择 Microsoft>应用注册>新注册。 
- 在 注册应用程序 页: - 输入应用注册 名称。  可能需要使用名称 native-quickstart将此名称与后端服务使用的名称区分开来。
- 选择任何组织目录中的 帐户(任何Microsoft Entra 目录 - 多租户)和个人Microsoft帐户(例如 Skype、Xbox)。
- 在 重定向 URI:- 选择 公共客户端(移动 & 桌面)
- 输入 URL quickstart://auth
 
 
- 输入应用注册 名称。  可能需要使用名称 
- 选择“注册”。 
- 选择“API 权限”“添加权限”>“我的 API”。 
- 选择之前为后端服务创建的应用注册。 如果未看到应用注册,请确保已添加 access_as_user 范围。   
- 在 选择权限下,选择 access_as_user,然后选择 添加权限。 
- 选择 身份验证>移动和桌面应用程序。 
- 选中 - https://login.microsoftonline.com/common/oauth2/nativeclient旁边的框。
- 选中 - msal{client-id}://auth旁边的框(将- {client-id}替换为应用程序 ID)。
- 选择“添加 URI,然后在字段中为额外的 URI 添加 - http://localhost。
- 选择页面底部的“保存” 。 
- 选择“概述”。 记下 应用程序(客户端)ID(称为 本机客户端应用程序 ID),因为需要它来配置移动应用。 
我们定义了三个重定向 URL:
- WPF 应用程序使用 http://localhost。
- UWP 应用程序使用 https://login.microsoftonline.com/common/oauth2/nativeclient。
- 移动(Android 和 iOS)应用程序使用 msal{client-id}://auth。
将Microsoft标识客户端添加到应用
在 Visual Studio 中打开 TodoApp.sln 解决方案,并将 TodoApp.Android 项目设置为启动项目。  将 Microsoft标识库(MSAL) 添加到 TodoApp.Android 项目中:
将 Microsoft标识库(MSAL) 添加到平台项目中:
- 右键单击项目,然后选择 管理 NuGet 包...。 
- 选择“浏览”选项卡。 
- 在搜索框中输入 - Microsoft.Identity.Client,然后按 Enter。
- 选择 - Microsoft.Identity.Client结果,然后单击 安装。  
- 接受许可协议以继续安装。 
将本机客户端 ID 和后端范围添加到配置。
打开 TodoApp.Data 项目并编辑 Constants.cs 文件。 为 ApplicationId 和 Scopes添加常量:
  public static class Constants
  {
      /// <summary>
      /// The base URI for the Datasync service.
      /// </summary>
      public static string ServiceUri = "https://demo-datasync-quickstart.azurewebsites.net";
      /// <summary>
      /// The application (client) ID for the native app within Microsoft Entra ID
      /// </summary>
      public static string ApplicationId = "<client-id>";
      /// <summary>
      /// The list of scopes to request
      /// </summary>
      public static string[] Scopes = new[]
      {
          "<scope>"
      };
  }
将 <client-id> 替换为在Microsoft Entra ID 中注册客户端应用程序时收到的  本机客户端应用程序 ID,并将 <scope> 替换为在注册服务应用程序时使用 公开 API 时复制的 Web API 范围。
在 TodoApp.Android 项目中打开 MainActivity.cs 文件。  在文件的顶部,添加以下 using 语句:
using Android.Content;
using Microsoft.Identity.Client;
using Microsoft.Datasync.Client;
using System.Linq;
using System.Threading.Tasks;
using Debug = System.Diagnostics.Debug;
在 MainActivity 类的顶部,添加以下字段:
public IPublicClientApplication identityClient;
在 OnCreate() 方法中,更改 TodoService的定义:
TodoService = new RemoteTodoService(GetAuthenticationToken);
添加以下代码以定义 GetAuthenticationToken() 方法:
public async Task<AuthenticationToken> GetAuthenticationToken()
{
    if (identityClient == null)
    {
        identityClient = PublicClientApplicationBuilder.Create(Constants.ApplicationId)
            .WithAuthority(AzureCloudInstance.AzurePublic, "common")
            .WithRedirectUri($"msal{Constants.ApplicationId}://auth")
            .WithParentActivityOrWindow(() => this)
            .Build();
    }
    var accounts = await identityClient.GetAccountsAsync();
    AuthenticationResult result = null;
    bool tryInteractiveLogin = false;
    try
    {
        result = await identityClient
            .AcquireTokenSilent(Constants.Scopes, accounts.FirstOrDefault())
            .ExecuteAsync();
    }
    catch (MsalUiRequiredException)
    {
        tryInteractiveLogin = true;
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"MSAL Silent Error: {ex.Message}");
    }
    if (tryInteractiveLogin)
    {
        try
        {
            result = await identityClient
                .AcquireTokenInteractive(Constants.Scopes)
                .ExecuteAsync()
                .ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"MSAL Interactive Error: {ex.Message}");
        }
    }
    return new AuthenticationToken
    {
        DisplayName = result?.Account?.Username ?? "",
        ExpiresOn = result?.ExpiresOn ?? DateTimeOffset.MinValue,
        Token = result?.AccessToken ?? "",
        UserId = result?.Account?.Username ?? ""
    };
}
              GetAuthenticationToken() 方法适用于Microsoft标识库(MSAL),以获取适合将登录用户授权到后端服务的访问令牌。  然后,此函数将传递给用于创建客户端的 RemoteTodoService。  如果身份验证成功,则会生成 AuthenticationToken,其中包含授权每个请求所需的数据。  否则,将生成过期的错误的令牌。
通过添加以下方法处理标识客户端的回调:
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);
    // Return control to MSAL
    AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(requestCode, resultCode, data);
}
使用以下代码创建新类 MsalActivity:
using Android.App;
using Android.Content;
using Microsoft.Identity.Client;
namespace TodoApp.Android
{
    [Activity(Exported = true)]
    [IntentFilter(new[] { Intent.ActionView },
        Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault },
        DataHost = "auth",
        DataScheme = "msal{client-id}")]
    public class MsalActivity : BrowserTabActivity
    {
    }
}
将 {client-id} 替换为本机客户端的应用程序 ID(与 Constants.ApplicationId相同)。
如果项目面向 Android 版本 11(API 版本 30)或更高版本,则必须更新 AndroidManifest.xml 以满足 Android 包可见性要求。  打开 Properties/AndroidManifest.xml,将以下 queries/intent 节点添加到 manifest 节点:
<manifest>
  ...
  <queries>
    <intent>
      <action android:name="android.support.customtabs.action.CustomTabsService" />
    </intent>
  </queries>
</manifest>
测试应用
运行或重启应用。
应用运行时,将打开浏览器以要求进行身份验证。 如果之前尚未向应用进行身份验证,应用会要求你同意。 身份验证完成后,系统浏览器将关闭,应用会像以前一样运行。
后续步骤
接下来,实现脱机存储,将应用程序配置为脱机运行。
进一步阅读
- 快速入门:使用Microsoft标识平台 保护 Web API
- 使用 MSAL.NET Xamarin Android 的配置要求和故障排除提示
- 方案:用于调用 Web API 的移动应用程序