.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
相关产品推荐
相关产品推荐

