如果在应用中使用 Microsoft 身份验证库 (MSAL) 进行身份验证

若要以编程方式向群集进行身份验证,你需要从 Microsoft Entra ID 请求特定于 Azure 数据资源管理器的访问令牌。 在向群集发出请求时,此访问令牌充当身份证明。 可使用 Microsoft 身份验证库 (MSAL)之一来创建访问令牌。

本文介绍如何使用 MSAL 对群集的主体进行身份验证。 直接使用 MSAL 对主体进行身份验证主要适用于需要代理 (OBO) 身份验证单页应用程序 (SPA) 身份验证的 Web 应用程序。 对于其他情况,建议使用 Kusto 客户端库,因为它们简化了身份验证过程。

在本文中,了解主要的身份验证场景、为成功通过身份验证而提供的信息,以及使用 MSAL 进行身份验证的情况。

身份验证方案

主要身份验证场景如下所示:

对于用户和应用程序身份验证,建议使用 Kusto 客户端库。 对于 OBO 和 SPA 身份验证,不能使用 Kusto 客户端库。

身份验证参数

在令牌获取过程中,客户端需要提供以下参数:

参数名称 说明
资源 ID 要为其颁发 Microsoft Entra 访问令牌的资源 ID。 资源 ID 是不包含端口信息和路径的群集 URI。

示例:help 群集的资源 ID 是 https://help.chinaeast2.kusto.chinacloudapi.cn
Microsoft Entra 租户 ID Microsoft Entra ID 是一项多租户服务,每个组织都可以创建一个名为“目录”的对象,用来保存与安全相关的对象(例如用户帐户和应用程序)。 Microsoft Entra ID 通常将目录称为“租户”。 每个租户都有一个 GUID 形式的租户 ID。 在许多情况下,组织的域名还可用于标识 Microsoft Entra 租户。

示例:“Contoso”组织可能具有租户 ID 12345678-a123-4567-b890-123a456b789c 和域名 contoso.com
Microsoft Entra 颁发机构 URI 用于身份验证的终结点。 Microsoft Entra 目录或租户确定 Microsoft Entra 颁发机构 URI。 URI 是 https://login.partner.microsoftonline.cn/{tenantId},其中 {tenantId} 是租户 ID 或域名。

示例:例如 https://login.partner.microsoftonline.cn/12345678-a123-4567-b890-123a456b789c

注意

Microsoft Entra 服务终结点在国家云中会变化。 使用部署在国家云中的 Azure 数据资源管理器服务时,请设置相应的国家云 Microsoft Entra 服务终结点。

使用 MSAL 执行用户身份验证

以下代码示例演示如何使用 MSAL 获取群集的授权令牌。 授权是通过启动交互式登录 UI 的方式完成的。 appRedirectUri 是身份验证成功完成后 Microsoft Entra 重定向到的 URL。 MSAL 会从此重定向中提取授权代码。

var kustoUri = "https://<clusterName>.<region>.kusto.chinacloudapi.cn";

var authClient = PublicClientApplicationBuilder.Create("<appId>")
    .WithAuthority($"https://login.partner.microsoftonline.cn/<appTenant>")
    .WithRedirectUri("<appRedirectUri>")
    .Build();

var result = authClient.AcquireTokenInteractive(
    new[] { $"{kustoUri}/.default" } // Define scopes for accessing Azure Data Explorer cluster
).ExecuteAsync().Result;

var bearerToken = result.AccessToken;

var request = WebRequest.Create(new Uri(kustoUri));
request.Headers.Set(HttpRequestHeader.Authorization, string.Format(CultureInfo.InvariantCulture, "{0} {1}", "Bearer", bearerToken));

注意

  • 建议尽可能地使用 Kusto 客户端库。 这些库让你能够在 Kusto 连接字符串中提供身份验证属性,从而简化了身份验证过程。
  • 使用 Kusto 客户端库时,Microsoft Entra 令牌存储在用户计算机上的本地令牌缓存中,以减少提示用户输入凭据的次数。 缓存文件为 %APPDATA%\Kusto\userTokenCache.data,只有已登录的用户可访问它。

使用 MSAL 执行应用程序身份验证

以下代码示例演示如何使用 MSAL 获取群集的授权令牌。 在此流中,不会显示任何提示。 应用程序必须注册到 Microsoft Entra ID,并且具有由 Microsoft Entra ID 颁发的应用密钥或 X509v2 证书。 若要设置应用程序,请参阅预配 Microsoft Entra 应用程序

var kustoUri = "https://<clusterName>.<region>.kusto.chinacloudapi.cn";

var authClient = ConfidentialClientApplicationBuilder.Create("<appId>")
    .WithAuthority($"https://login.partner.microsoftonline.cn/<appTenant>")
    .WithClientSecret("<appKey>") // Can be replaced by .WithCertificate to authenticate with an X.509 certificate
    .Build();

var result = authClient.AcquireTokenForClient(
    new[] { $"{kustoUri}/.default" } // Define scopes for accessing Azure Data Explorer cluster
).ExecuteAsync().Result;
var bearerToken = result.AccessToken;

var request = WebRequest.Create(new Uri(kustoUri));
request.Headers.Set(HttpRequestHeader.Authorization, string.Format(CultureInfo.InvariantCulture, "{0} {1}", "Bearer", bearerToken));

注意

建议尽可能地使用 Kusto 客户端库。 这些库让你能够在 Kusto 连接字符串中提供身份验证属性,从而简化了身份验证过程。

执行代理 (OBO) 身份验证

当 Web 应用程序或服务充当用户/应用程序与群集之间的中介时,适合使用代理身份验证

在此情况中,会向应用程序发送任意资源的 Microsoft Entra 访问令牌。 接着,该应用程序使用该令牌为 Azure 数据资源管理器资源获取新的 Microsoft Entra 访问令牌。 然后,该应用程序可代表原始 Microsoft Entra 访问令牌指示的主体来访问群集。 此流被称为 OAuth 2.0 代理身份验证流。 它通常需要执行与 Microsoft Entra 相关的多个配置步骤,在某些情况下,还可能需要取得 Microsoft Entra 租户管理员的专门同意。

若要执行代理身份验证,请执行以下操作:

  1. 预配 Microsoft Entra 应用程序

  2. 在应用程序和群集之间建立信任关系。 为此,请按照配置委托的权限中的步骤操作。

  3. 在服务器代码中,使用 MSAL 执行令牌交换。

    var kustoUri = "https://<clusterName>.<region>.kusto.chinacloudapi.cn";
    
    var authClient = ConfidentialClientApplicationBuilder.Create("<appId>")
        .WithAuthority($"https://login.partner.microsoftonline.cn/<appTenant>")
        .WithClientSecret("<appKey>") // Can be replaced by .WithCertificate to authenticate with an X.509 certificate
        .Build();
    
    var result = authClient.AcquireTokenOnBehalfOf(
        new[] { $"{kustoUri}/.default" }, // Define scopes for accessing your cluster
        new UserAssertion("<userAccessToken>") // Encode the "original" token that will be used for exchange
    ).ExecuteAsync().Result;
    var accessTokenForAdx = result.AccessToken;
    
  4. 使用令牌运行查询。 例如:

    var request = WebRequest.Create(new Uri(kustoUri));
    request.Headers.Set(HttpRequestHeader.Authorization, string.Format(CultureInfo.InvariantCulture, "{0} {1}", "Bearer", accessTokenForAdx));
    

执行单页应用程序 (SPA) 身份验证

对于 SPA Web 客户端的身份验证,请使用 OAuth 授权代码流

在此方案中,将重定向应用以登录到 Microsoft Entra ID。 然后,Microsoft Entra ID 使用 URI 中的授权代码重定向回应用。 然后,应用向令牌终结点发出请求以获取访问令牌。 该令牌在 24 小时内有效,在此期间,客户端可以通过以无提示方式获取令牌来重用该令牌。

Microsoft 标识平台提供了针对不同用例(例如 ReactAngularJavaScript)的详细教程。

若要为 Web 客户端设置身份验证,请执行以下操作:

  1. 预配 Microsoft Entra 应用程序

  2. 按照采用身份验证代码流的 MSAL.js 2.0 中所述配置应用。

  3. 使用 MSAL.js 2.0 库将用户登录,并向群集进行身份验证。 Microsoft 标识平台提供了针对不同用例(例如 ReactAngularJavaScript)的详细教程。

    以下示例使用 MSAL.js 库来访问 Azure 数据资源管理器。

    import * as msal from "@azure/msal-browser";
    
    const msalConfig = {
      auth: {
        clientId: "<AAD client application ID>",
        authority: "https://login.partner.microsoftonline.cn/<AAD tenant ID>",
      },
    };
    
    const msalInstance = new msal.PublicClientApplication(msalConfig);
    const myAccounts = msalInstance.getAllAccounts();
    
    // If no account is logged in, redirect the user to log in.
    if (myAccounts === undefined || myAccounts.length === 0) {
      try {
        await msalInstance.loginRedirect({
          scopes: ["https://help.chinaeast2.kusto.chinacloudapi.cn/.default"],
        });
      } catch (err) {
        console.error(err);
      }
    }
    const account = myAccounts[0];
    const name = account.name;
    window.document.getElementById("main").innerHTML = `Hi ${name}!`;
    
    // Get the access token required to access the specified Azure Data Explorer cluster.
    const accessTokenRequest = {
      account,
      scopes: ["https://help.chinaeast2.kusto.chinacloudapi.cn/.default"],
    };
    let acquireTokenResult = undefined;
    try {
      acquireTokenResult = await msalInstance.acquireTokenSilent(accessTokenRequest);
    } catch (error) {
      if (error instanceof InteractionRequiredAuthError) {
        await msalInstance.acquireTokenRedirect(accessTokenRequest);
      }
    }
    
    const accessToken = acquireTokenResult.accessToken;
    
    // Make requests to the specified cluster with the token in the Authorization header.
    const fetchResult = await fetch("https://help.chinaeast2.kusto.chinacloudapi.cn/v2/rest/query", {
      headers: {
        Authorization: `Bearer ${accessToken}`,
        "Content-Type": "application/json",
      },
      method: "POST",
      body: JSON.stringify({
        db: "Samples",
        csl: "StormEvents | count",
      }),
    });
    const jsonResult = await fetchResult.json();
    
    // The following line extracts the first cell in the result data.
    const count = jsonResult.filter((x) => x.TableKind === "PrimaryResult")[0].Rows[0][0];