如何确保ClaimsPrincipal正确反序列化?解决序列化属性丢失问题
解决ClaimsPrincipal/ClaimsIdentity序列化后反序列化属性丢失的问题
我之前也踩过这个坑,本质原因是:ClaimsPrincipal和ClaimsIdentity属于.NET安全体系的特殊类型,它们的核心数据(比如Claims集合)并不是常规的可读写公共属性,而是通过内部逻辑维护的。Json.NET默认的序列化机制只能识别普通公共属性,直接序列化会丢失关键数据,反序列化自然就得到空对象了。
下面是具体的解决办法——通过自定义Json转换器来处理这两个类型的序列化与反序列化:
第一步:实现ClaimsIdentity的自定义转换器
这个转换器负责处理ClaimsIdentity的序列化逻辑,把它的核心数据(声明集合、Claim类型配置等)转换成可被Json.NET识别的结构,反序列化时再重新构建对象:
public class ClaimsIdentityConverter : JsonConverter<ClaimsIdentity> { public override ClaimsIdentity ReadJson(JsonReader reader, Type objectType, ClaimsIdentity existingValue, bool hasExistingValue, JsonSerializer serializer) { var jsonObject = JObject.Load(reader); var claims = jsonObject["Claims"].ToObject<List<Claim>>(serializer); var identity = new ClaimsIdentity(claims); // 还原额外属性(按需添加) identity.NameClaimType = jsonObject["NameClaimType"]?.ToString() ?? ClaimsIdentity.DefaultNameClaimType; identity.RoleClaimType = jsonObject["RoleClaimType"]?.ToString() ?? ClaimsIdentity.DefaultRoleClaimType; identity.IsAuthenticated = jsonObject["IsAuthenticated"]?.Value<bool>() ?? false; return identity; } public override void WriteJson(JsonWriter writer, ClaimsIdentity value, JsonSerializer serializer) { var jsonObject = new JObject { ["Claims"] = JArray.FromObject(value.Claims, serializer), ["NameClaimType"] = value.NameClaimType, ["RoleClaimType"] = value.RoleClaimType, ["IsAuthenticated"] = value.IsAuthenticated }; jsonObject.WriteTo(writer); } }
第二步:实现ClaimsPrincipal的自定义转换器
ClaimsPrincipal本质是多个ClaimsIdentity的集合,所以转换器的逻辑就是处理身份集合的序列化与还原:
public class ClaimsPrincipalConverter : JsonConverter<ClaimsPrincipal> { public override ClaimsPrincipal ReadJson(JsonReader reader, Type objectType, ClaimsPrincipal existingValue, bool hasExistingValue, JsonSerializer serializer) { var jsonObject = JObject.Load(reader); var identities = jsonObject["Identities"].ToObject<List<ClaimsIdentity>>(serializer); return new ClaimsPrincipal(identities); } public override void WriteJson(JsonWriter writer, ClaimsPrincipal value, JsonSerializer serializer) { var jsonObject = new JObject { ["Identities"] = JArray.FromObject(value.Identities, serializer) }; jsonObject.WriteTo(writer); } }
第三步:使用自定义转换器完成序列化与反序列化
修改你的原有代码,把两个转换器加入到JsonSerializerSettings中,让Json.NET用自定义逻辑处理这两个类型:
var identity = new ClaimsIdentity(new List<Claim>() { new Claim("Role", "Admin") }); var principal = new ClaimsPrincipal(identity); var settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, Converters = new List<JsonConverter> { new ClaimsIdentityConverter(), new ClaimsPrincipalConverter() } }; // 序列化 string serialized = JsonConvert.SerializeObject(principal, settings); // 反序列化 ClaimsPrincipal deserialized = JsonConvert.DeserializeObject<ClaimsPrincipal>(serialized, settings); // 现在可以正常访问属性了 var roleClaim = deserialized.Claims.FirstOrDefault(c => c.Type == "Role"); Console.WriteLine(roleClaim?.Value); // 输出:Admin
补充说明
- 如果需要还原
ClaimsIdentity的更多属性(比如Name、AuthenticationType),可以在转换器的ReadJson和WriteJson方法中添加对应的字段处理; - 这个方案支持所有类型的Claim,不止局限于
Role声明。
内容的提问来源于stack exchange,提问作者Cristian E.
相关产品推荐
相关产品推荐

