本文介绍如何在 Azure 应用服务中使用 内置身份验证和授权 时使用用户标识。
先决条件
在 已启用应用服务身份验证/授权模块的 Azure 应用服务上运行的 Web 应用程序。
在应用代码中访问用户声明
应用的经过身份验证的最终用户或客户端应用程序在传入令牌中发出声明。 应用服务通过将声明注入请求标头,使这些声明可供代码使用。 不允许外部请求设置这些标头,因此仅在应用服务设置它们时才存在。
可以使用应用服务身份验证提供的声明信息在应用代码中执行授权检查。 任何语言或框架中的代码都可以从请求标头获取所需的信息。 某些代码框架提供了可能更方便的额外选项。 请参阅 特定于框架的替代方法。
下表介绍了一些示例标头:
标头 | 说明 |
---|---|
X-MS-CLIENT-PRINCIPAL |
可用声明的 Base64 编码 JSON 表示格式。 有关详细信息,请参阅 解码客户端主体标头。 |
X-MS-CLIENT-PRINCIPAL-ID |
标识提供者为调用方设置的标识符。 |
X-MS-CLIENT-PRINCIPAL-NAME |
标识提供者为调用方设置的可读名称,例如电子邮件地址或用户主体名称。 |
X-MS-CLIENT-PRINCIPAL-IDP |
应用服务身份验证使用的标识提供者的名称。 |
提供者令牌也通过类似的标头公开。 例如,Microsoft Entra 还根据需要设置 X-MS-TOKEN-AAD-ACCESS-TOKEN
和 X-MS-TOKEN-AAD-ID-TOKEN
。
注意
应用服务使请求标头可用于所有语言框架。 不同的语言框架可能会以不同格式(例如小写或词首字母大写)向应用代码显示这些标头。
解码客户端主体标头
标头 X-MS-CLIENT-PRINCIPAL
包含 Base64 编码 JSON 中的完整可用声明集。 若要处理此标头,应用必须解码有效负载并循环访问 claims
数组以查找相关的声明。
注意
这些声明将经历默认声明映射过程,因此某些名称可能与令牌中显示的名称不同。
解码的有效负载结构如下所示:
{
"auth_typ": "",
"claims": [
{
"typ": "",
"val": ""
}
],
"name_typ": "",
"role_typ": ""
}
下表描述了这些属性。
properties | 类型 | 说明 |
---|---|---|
auth_typ |
字符串 | 应用服务身份验证使用的标识提供者的名称。 |
claims |
数组 | 一个对象的数组,这些对象表示可用的声明。 每个对象包含 typ 和 val 属性。 |
typ |
字符串 | 声明的名称,该名称可能受默认声明映射的约束,并且与令牌中的相应声明不同。 |
val |
字符串 | 声明的值。 |
name_typ |
字符串 | 名称声明类型,通常是一个 URI,它提供有关 name 声明的方案信息(如果有定义)。 |
role_typ |
字符串 | 角色声明类型,通常是一个 URI,它提供有关 role 声明的方案信息(如果有定义)。 |
为方便起见,可以将声明转换为应用语言框架使用的表示形式。 以下 C# 示例构造了应用程序使用的ClaimsPrincipal
类型。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Http;
public static class ClaimsPrincipalParser
{
private class ClientPrincipalClaim
{
[JsonPropertyName("typ")]
public string Type { get; set; }
[JsonPropertyName("val")]
public string Value { get; set; }
}
private class ClientPrincipal
{
[JsonPropertyName("auth_typ")]
public string IdentityProvider { get; set; }
[JsonPropertyName("name_typ")]
public string NameClaimType { get; set; }
[JsonPropertyName("role_typ")]
public string RoleClaimType { get; set; }
[JsonPropertyName("claims")]
public IEnumerable<ClientPrincipalClaim> Claims { get; set; }
}
public static ClaimsPrincipal Parse(HttpRequest req)
{
var principal = new ClientPrincipal();
if (req.Headers.TryGetValue("x-ms-client-principal", out var header))
{
var data = header[0];
var decoded = Convert.FromBase64String(data);
var json = Encoding.UTF8.GetString(decoded);
principal = JsonSerializer.Deserialize<ClientPrincipal>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
}
此时,代码可以循环访问 principal.Claims
以检查声明作为验证的一部分。 或者,可以转换为 principal.Claims
标准对象,并在请求管道中稍后使用它来执行这些检查。 还可以使用该对象来关联用户数据和其他用途。
函数的其余部分执行此转换,以创建一个可供其他 .NET 代码使用的 ClaimsPrincipal
。
var identity = new ClaimsIdentity(principal.IdentityProvider, principal.NameClaimType, principal.RoleClaimType);
identity.AddClaims(principal.Claims.Select(c => new Claim(c.Type, c.Value)));
return new ClaimsPrincipal(identity);
}
}
特定于框架的替代方案
对于 ASP.NET 4.6 应用,应用服务使用经过身份验证的用户声明填充
ClaimsPrincipal.Current
。 可以遵循标准 .NET 代码模式,包括[Authorize]
属性。ClaimsPrincipal.Current
未在 Azure Functions 中为 .NET 代码填充,但仍可以在请求标头中找到用户声明,或者从请求上下文或通过绑定参数获取ClaimsPrincipal
对象。 有关详细信息,请参阅 使用 Azure Functions 中的客户端标识。对于 PHP 应用,应用服务同样填充变量
_SERVER['REMOTE_USER']
。对于 Java 应用,可从 Tomcat servlet 访问声明。
对于 .NET Core,
Microsoft.Identity.Web
支持使用应用服务身份验证填充当前用户。 有关详细信息,请参阅 与使用 Microsoft.Identity.Web 运行的 Web 应用的 Azure 应用服务身份验证集成。 有关访问 Microsoft Graph 的 Web 应用的演示,请参阅 教程:以用户身份从受保护的 .NET 应用访问 Microsoft Graph。
注意
为使声明映射功能正常工作,您必须为应用程序启用令牌存储。
使用 API 访问用户声明
如果为应用启用了 令牌存储 ,还可以调用 /.auth/me
以获取有关经过身份验证的用户的其他详细信息。