教程:以用户身份从受保护的 .NET 应用访问 Azure Graph

了解如何从 Azure 应用服务上运行的 Web 应用访问 Microsoft Graph。

显示访问 Microsoft Graph 的示意图。

你希望从 Web 应用添加对 Microsoft Graph 的访问权限,并以已登录用户的身份执行一些操作。 本部分介绍如何向 Web 应用授予委托的权限,以及如何从 Microsoft Entra ID 获取已登录用户的配置文件信息。

本教程介绍如何执行下列操作:

  • 向 Web 应用授予委托的权限。
  • 为已登录的用户从 Web 应用调用 Microsoft Graph。

如果没有 Azure 订阅,可在开始前创建一个试用帐户

先决条件

授予前端访问权限以调用 Microsoft Graph

在 Web 应用上启用身份验证和授权后,Web 应用将注册到Microsoft标识平台,并由 Microsoft Entra 应用程序提供支持。 在此步骤中,授予 Web 应用相应权限,为用户访问 Microsoft Graph。

注意

从技术角度讲,你为 Web 应用的 Microsoft Entra 应用程序授予权限,以便它能够访问用户的 Microsoft Graph 中的 Microsoft Entra 应用程序。

  1. Microsoft Entra 管理中心 中,选择 Entra ID

  2. 选择“应用注册”“拥有的应用程序”>“查看此目录中的所有应用程序”。 选择你的 Web 应用名称,然后选择“API 权限”。

  3. 选择 “添加权限”,然后选择 “Microsoft API”,然后选择 “Microsoft Graph”。

  4. 选择“委托的权限”,然后从列表中选择“User.Read” 。 选择“添加权限”。

对应用服务进行配置,使之返回可用的访问令牌

Web 应用现在具有以已登录用户身份访问 Microsoft Graph 所需的权限。 在本部分中,将配置应用服务身份验证和授权,以便提供用于访问 Microsoft Graph 的可用访问令牌。 在此步骤中,需要为下游服务 (Microsoft Graph) 添加 User.Read 范围:https://microsoftgraph.chinacloudapi.cn/User.Read

重要

如果未将应用服务配置为返回可用的访问令牌,则在代码中调用 Microsoft 图形 API 时会收到 CompactToken parsing failed with error code: 80049217 错误。

使用 Azure CLI 调用应用服务 Web 应用 REST API 以获取更新身份验证配置设置,使 Web 应用能够调用 Microsoft Graph。

  1. 打开命令窗口并登录到 Azure CLI:

    az login
    
  2. 获取现有的“config/authsettingsv2”设置,并将其保存到本地 authsettings.json 文件中。

    az rest --method GET --url '/subscriptions/{SUBSCRIPTION_ID}/resourceGroups/{RESOURCE_GROUP}/providers/Microsoft.Web/sites/{WEBAPP_NAME}/config/authsettingsv2/list?api-version=2020-06-01' > authsettings.json
    
  3. 使用偏好的文本编辑器打开 authsettings.json 文件。

  4. 查找 identityProviders> 的登录部分。

  5. 添加以下“loginParameters”设置:"loginParameters":[ "response_type=code id_token","scope=openid offline_access profile https://microsoftgraph.chinacloudapi.cn/User.Read" ]

    "identityProviders": {
        "azureActiveDirectory": {
          "enabled": true,
          "login": {
            "loginParameters":[
              "response_type=code id_token",
              "scope=openid offline_access profile https://microsoftgraph.chinacloudapi.cn/User.Read"
            ]
          }
        }
      }
    },
    
  6. 保存对文件 authsettings.json 所做的更改,并将本地设置上传到你的 Web 应用:

    az rest --method PUT --url '/subscriptions/{SUBSCRIPTION_ID}/resourceGroups/{RESOURCE_GROUP}/providers/Microsoft.Web/sites/{WEBAPP_NAME}/config/authsettingsv2?api-version=2020-06-01' --body @./authsettings.json
    

使用 .NET 调用 Azure Graph

Web 应用现在具有所需的权限,并且还将 Microsoft Graph 的客户端 ID 添加到登录参数中。

使用 Microsoft.Identity.Web 库,Web 应用将获取一个访问令牌,用于向 Microsoft Graph 进行身份验证。 在 1.2.0 版和更高版本中,Microsoft.Identity.Web 库与应用服务身份验证/授权模块集成,且可与之一起运行。 Microsoft.Identity.Web 检测到 Web 应用托管在应用服务中,并自动从 X-MS-TOKEN-AAD-ACCESS-TOKEN 应用服务注入的请求标头中检索访问令牌。 无需在代码中手动访问此标头。 然后,使用 Microsoft 图形 API 将访问令牌传递给经过身份验证的请求。

若要查看作为示例应用程序组成部分的这段代码,请参阅:

注意

Web 应用无需 Microsoft.Identity.Web 库即可进行基本身份验证/授权或在 Azure Graph 中对请求进行身份验证。

但是,应用服务身份验证/授权旨在用于更基本的身份验证场景。 对于更复杂的场景(例如处理自定义声明),需要 Microsoft.Identity.Web 库或 Microsoft 身份验证库。 在一开始就有更多的设置和配置工作,但 Microsoft.Identity.Web 库可与应用服务身份验证/授权模块同时运行。 稍后,当 Web 应用需要处理更复杂的方案时,可以禁用应用服务身份验证/授权模块,并Microsoft。Identity.Web 已是应用的一部分。

安装客户端库包

使用 .NET Core 命令行接口或 Visual Studio 中的包管理器控制台,在项目中安装 Microsoft.Identity.WebMicrosoft.Identity.Web.MicrosoftGraph NuGet 包。

.NET Core 命令行

打开一个命令行,并切换到包含项目文件的目录。

运行安装命令。

dotnet add package Microsoft.Identity.Web.MicrosoftGraph

dotnet add package Microsoft.Identity.Web

程序包管理器控制台

在 Visual Studio 中打开项目/解决方案,并使用“工具”“NuGet 包管理器”>“包管理器控制台”命令打开控制台。

运行安装命令。

Install-Package Microsoft.Identity.Web.GraphServiceClient

Install-Package Microsoft.Identity.Web

Startup.cs

在 Startup.cs 文件中, 方法会将 Microsoft.Identity.Web 添加到 Web 应用。 AddMicrosoftGraph 方法添加 Azure Graph 支持。 有关管理增量同意和条件访问的信息, 请阅读本文

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Identity.Web;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;

// Some code omitted for brevity.
public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddOptions();
      string[] initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');

      services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
              .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
              .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
                      .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
                      .AddInMemoryTokenCaches(); 

      services.AddAuthorization(options =>
      {
          // By default, all incoming requests will be authorized according to the default policy
          options.FallbackPolicy = options.DefaultPolicy;
      });
      services.AddRazorPages()
          .AddMvcOptions(options => {})                
          .AddMicrosoftIdentityUI();

      services.AddControllersWithViews()
              .AddMicrosoftIdentityUI();
    }
}

appsettings.json

AzureAd 指定 Microsoft.Identity.Web 库的配置。 在 Microsoft Entra 管理中心 中,从门户菜单中选择 Entra ID,然后选择 应用注册。 选择启用应用服务身份验证/授权模块时创建的应用注册。 (该应用注册应具有与 Web 应用相同的名称。)可在应用注册概述页面中找到租户 ID 和客户 ID。 可以在租户的 Microsoft Entra 概述页面中找到域名。

Graph 指定 Azure Graph 终结点以及应用所需的初始范围。

{
  "AzureAd": {
    "Instance": "https://login.chinacloudapi.cn/",
    "Domain": "[Enter the domain of your tenant, e.g. contoso.partner.onmschina.cn]",
    "TenantId": "[Enter 'common', or 'organizations' or the Tenant Id (Obtained from the Microsoft Entra admin center. Select 'Endpoints' from the 'App registrations' blade and use the GUID in any of the URLs), e.g. da41245a5-11b3-996c-00a8-4d99re19f292]",
    "ClientId": "[Enter the Client Id (Application ID obtained from the Microsoft Entra admin center), e.g. ba74781c2-53c2-442a-97c2-3d60re42f403]",
    "ClientSecret": "[Copy the client secret added to the app from the Microsoft Entra admin center]",
    "ClientCertificates": [
    ],
    // the following is required to handle Continuous Access Evaluation challenges
    "ClientCapabilities": [ "cp1" ],
    "CallbackPath": "/signin-oidc"
  },
  "DownstreamApis": {
    "MicrosoftGraph": {
      // Specify BaseUrl if you want to use Microsoft graph in a national cloud.
      // See https://learn.microsoft.com/graph/deployments#microsoft-graph-and-graph-explorer-service-root-endpoints
      // "BaseUrl": "https://microsoftgraph.chinacloudapi.cn/v1.0",

      // Set RequestAppToken this to "true" if you want to request an application token (to call graph on 
      // behalf of the application). The scopes will then automatically
      // be ['https://microsoftgraph.chinacloudapi.cn/.default'].
      // "RequestAppToken": false

      // Set Scopes to request (unless you request an app token).
      "Scopes": [ "User.Read" ]

      // See https://aka.ms/ms-id-web/downstreamApiOptions for all the properties you can set.
    }
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

以用户身份调用 Microsoft Graph

下面的示例演示如何以已登录用户身份调用 Microsoft Graph 并获取一些用户信息。 GraphServiceClient 对象已注入到控制器中,并且 Microsoft.Identity.Web 库已为你配置了身份验证。

// Index.cshtml.cs
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Graph;
using System.IO;
using Microsoft.Identity.Web;
using Microsoft.Extensions.Logging;

// Some code omitted for brevity.

[AuthorizeForScopes(Scopes = new[] { "User.Read" })]
public class IndexModel : PageModel
{
    private readonly ILogger<IndexModel> _logger;
    private readonly GraphServiceClient _graphServiceClient;

    public IndexModel(ILogger<IndexModel> logger, GraphServiceClient graphServiceClient)
    {
        _logger = logger;
        _graphServiceClient = graphServiceClient;
    }

    public async Task OnGetAsync()
    {
        try
        {
            var user = await _graphServiceClient.Me.GetAsync();
            ViewData["Me"] = user;
            ViewData["name"] = user.DisplayName;

            using (var photoStream = await _graphServiceClient.Me.Photo.Content.GetAsync())
            {
                byte[] photoByte = ((MemoryStream)photoStream).ToArray();
                ViewData["photo"] = Convert.ToBase64String(photoByte);
            }
        }
        catch (Exception ex)
        {
            ViewData["photo"] = null;
        }
    }
}

清理资源

如果你完成了本教程(包含多个部分)中的所有步骤,那么就已在资源组中创建了应用服务、应用服务托管计划和存储帐户。 你还在 Microsoft Entra ID 中创建了应用注册。 如果选择了外部配置,则可能已创建新的外部租户。 如果不再需要这些资源和应用注册,请将其删除,以免继续产生费用。

本教程介绍如何执行下列操作:

  • 删除按照本教程创建的 Azure 资源。

删除资源组

  1. Azure 门户中的 Azure 门户菜单中,选择 “资源组”。

  2. 选择包含应用服务和应用服务计划的资源组。

  3. 选择“删除资源组”,删除该资源组和所有资源。

    显示如何删除资源组的屏幕截图。

此操作可能需要几分钟时间。

删除应用注册

  1. Microsoft Entra 管理中心 中,选择 应用注册。 然后,选择创建的应用程序。

    显示选择应用注册的屏幕截图。

  2. 在应用注册概述中,选择“删除”。

    显示删除应用注册的屏幕截图。

删除外部租户

如果创建了新的外部租户,则可以将其删除

  1. Microsoft Entra 管理中心,浏览到 Entra ID>概述>管理租户

  2. 选择要删除的租户,然后选择“删除”。

    可能需要先完成所需的操作,然后才能删除租户。 例如,可能需要删除租户中的所有用户流和应用注册。

  3. 如果已准备好删除租户,请选择“删除”。

后续步骤

在本教程中,你了解了如何执行以下操作:

  • 向 Web 应用授予委托的权限。
  • 为已登录的用户从 Web 应用调用 Microsoft Graph。