.NET 8中JWT Token解码异常:仅读取半数Claims
问题背景
示例JWT Token在jwt.io上可正常解析出包含sub、var1至var5共6个Claims的Payload,但在大型.NET 8 ASP.NET Core项目中执行以下代码仅能获取半数Claims:
var token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwidmFyMSI6IjEiLCJ2YXIyIjoiMiIsInZhcjMiOiIzIiwidmFyNCI6IjQiLCJ2YXI1IjoiNSJ9.2VmaLH12HitYv8T7Z9wp6dRk87p9krZXFm3XOW9ulEM"; var tokenHandler = new JwtSecurityTokenHandler(); var tokenS = tokenHandler.ReadJwtToken(token);
相同代码在全新.NET 8项目中可得到完整结果。调试.NET源码发现,JwtPayload.CreatePayload方法的while循环里,Utf8JsonReader的TokenType交替为PropertyName和String,导致跳过部分Claims;正常项目中TokenType始终为PropertyName,怀疑是System.Text.Json全局配置问题。
排查思路
验证全局JsonSerializerOptions影响
项目可能通过AddJsonOptions或其他方式修改了全局Json配置,比如自定义JsonConverter、特殊的大小写规则等。可手动解析Payload验证:var payloadBase64 = token.Split('.')[1]; // 处理Base64URL编码转换 var payloadBytes = Convert.FromBase64String(payloadBase64.Replace('-', '+').Replace('_', '/')); var payloadJson = Encoding.UTF8.GetString(payloadBytes); // 使用默认配置反序列化 var payloadDict = JsonSerializer.Deserialize<Dictionary<string, string>>(payloadJson, new JsonSerializerOptions());如果手动解析能拿到完整Claims,说明全局Json配置确实干扰了JWT库的内部解析。
排查自定义JwtSecurityTokenHandler或配置
检查项目是否注册了自定义的JwtSecurityTokenHandler,或在AddJwtBearer配置中添加了Claim筛选、转换逻辑。确保测试时使用默认实例化的JwtSecurityTokenHandler,不依赖DI容器中的自定义实例。检查NuGet包版本冲突
大型项目可能存在间接依赖版本冲突,比如System.IdentityModel.Tokens.Jwt与Microsoft.AspNetCore.Authentication.JwtBearer的版本兼容问题,或System.Text.Json被其他包降级。执行以下命令查看依赖树:dotnet list package --include-transitive确认
System.Text.Json为.NET 8对应版本(8.x),且无版本冲突。排查全局自定义JsonConverter
如果项目中注册了针对string类型的自定义JsonConverter,可能干扰Utf8JsonReader的Token读取逻辑。临时移除所有自定义Converter后重新测试解码,确认是否恢复正常。检查AOP或反射篡改解析逻辑
大型项目可能通过AOP拦截器、反射等方式修改JwtPayload.CreatePayload的行为。调试时查看调用栈,确认是否有自定义代码介入解析流程。
内容的提问来源于stack exchange,提问作者Marco Ortali

