You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 4.8中JWT含preferred_username但Claims显示为空的问题

问题描述

使用.NET 4.8搭配Angular v22开发应用,通过控制器方法登录时始终抛出“Email声明缺失”异常,但JWT令牌中preferred_username字段实际存在,且代码中尝试获取该字段的值却始终失败。


令牌验证代码

var identity = (ClaimsIdentity)User.Identity;

foreach (var claim in identity.Claims)
{
    Console.WriteLine($"{claim.Type} = {claim.Value}");
}

var email =
    identity.FindFirst("preferred_username")?.Value ??
    identity.FindFirst("upn")?.Value ??
    identity.FindFirst(ClaimTypes.Email)?.Value ??
    identity.FindFirst(ClaimTypes.Name)?.Value;

注:此处始终无法获取到email值


Azure JwtBearer配置代码

public void ConfigureAzureJwtBearer(IAppBuilder app)
{
    var tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
    var audience = ConfigurationManager.AppSettings["ida:Audience"];

    app.UseJwtBearerAuthentication(
            new JwtBearerAuthenticationOptions
            {
                AuthenticationMode = AuthenticationMode.Active,

                TokenValidationParameters = new TokenValidationParameters
                {
                    // 兼容v1和v2版本的颁发者格式
                    ValidIssuers = new[]
                    {
                $"https://sts.windows.net/{tenantId}/",
                $"https://login.microsoftonline.com/{tenantId}/v2.0"
                    },

                    ValidAudience = audience,
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,

                    NameClaimType = "preferred_username"
                }
            });
}

JWT令牌详情

{
  "aud": "xxxx90a4-952d-4383-XXXX-c21163af558b",
  "iss": "https://login.microsoftonline.com/xxxxxxx-xxxx-xx5f-xxxx-xxx6x8x8xcxa/v2.0",
  "iat": 1765359275,
  "nbf": 1765359275,
  "exp": 1765363175,
  "name": "XXXX XXXX",
  "nonce": "0X9b07a1-4a9d-XXXX-b198-ffa56e5XXXX",
  "oid": "eX89a664-30Xf-4f2a-XXXX-8c2c382bXXXX",
  "preferred_username": "xxx.xxx@xxx.com",
  "rh": "1.AVYAkVTyRPKxX069aT-G2Hj8WqSQT7MtlYNDpAPCEWOvVYueAGJWAA.",
  "sid": "xxxe2259-xxxx-8214-dxxx-945ede240669",
  "sub": "-8MtCiN3YUZYboOlZ9I0Vj6o7-sQNvKpLLyEpX1Bq0o",
  "tid": "44f25491-b1f2-4e5f-bd69-3f86d878fc5a",
  "uti": "oXwOhQKWc0iwNaHzqz8UAA",
  "ver": "2.0"
}

解决方案

问题根源在于.NET的JWT验证中间件默认会将JWT中的原始声明类型映射到.NET标准的ClaimTypes枚举值,加上你配置了NameClaimType = "preferred_username",导致preferred_username被映射为ClaimTypes.Name(对应URI:http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name),所以直接用"preferred_username"作为类型查找会失败。

方案1:修改声明获取逻辑

既然已经将NameClaimType设为preferred_username,直接通过ClaimTypes.Name获取值即可:

var email =
    identity.FindFirst(ClaimTypes.Name)?.Value ??
    identity.FindFirst("upn")?.Value ??
    identity.FindFirst(ClaimTypes.Email)?.Value;

方案2:关闭声明类型映射

如果希望保留JWT中的原始声明类型名称,在TokenValidationParameters中添加MapInboundClaims = false,这样中间件不会修改声明的类型:

TokenValidationParameters = new TokenValidationParameters
{
    // 原有配置...
    NameClaimType = "preferred_username",
    MapInboundClaims = false // 添加这一行
}

修改后,原代码中identity.FindFirst("preferred_username")就能正常获取到值。


内容的提问来源于stack exchange,提问作者Ashu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 16:05:06