ClaimsPrincipal序列化为Json的正确姿势

news2026/4/6 11:17:39
第二步理解三者的关系1.Claim声明的基本单元职责表示一个键值对形式的声明如name Alice、role Admin。不仅包含类型Type和值Value还携带元数据如ValueType值的数据类型如字符串、整数等默认为string。Issuer声明的颁发者如https://login.microsoft.com。OriginalIssuer原始颁发者用于联合身份场景。Properties可扩展的键值对字典用于存储额外信息。特点不可变性一旦创建其核心属性Type/Value通常不可更改。轻量级设计为数据载体无业务逻辑。语义丰富通过标准声明类型如ClaimTypes.Name、ClaimTypes.Role实现互操作性。2.ClaimsIdentity代表一个身份Identity职责封装一组相关的Claim构成一个完整的身份视图。提供身份的上下文信息AuthenticationType认证方式如Cookies、JwtBearer。IsAuthenticated是否已通过认证依赖AuthenticationType非空。Name通常映射自NameClaimType类型的声明默认为ClaimTypes.Name。RoleClaimType用于角色判断的声明类型默认为ClaimTypes.Role。关键行为AddClaim(Claim)动态添加声明常用于中间件或策略授权。FindFirst(string type)查找首个匹配类型的声明。HasClaim(...)检查是否存在特定声明。设计思想一个主体如用户可以有多个身份例如本地登录身份 社交账号身份。支持多身份源如 Windows 身份、JWT 身份、自定义身份。3.ClaimsPrincipal代表一个安全主体Principal职责代表当前用户或系统实体是安全上下文的顶层容器。包含一个或多个ClaimsIdentity对象通过Identities集合。提供便捷属性访问主身份Identity属性返回第一个ClaimsIdentity。聚合所有身份的声明Claims属性返回所有ClaimsIdentity中声明的并集。关键行为IsInRole(string role)检查是否属于某角色依据RoleClaimType声明。FindFirst(string claimType)跨所有身份查找首个匹配声明。HasClaim(...)检查是否存在指定声明。与线程上下文集成在 ASP.NET Core 中HttpContext.User的类型就是ClaimsPrincipal。可通过Thread.CurrentPrincipal.NET Framework或IHttpContextAccessor.NET Core访问。4. 三者协作关系ClaimsPrincipal │ ├── ClaimsIdentity #1 (e.g., from JWT) │ ├── Claim: { Typename, ValueAlice } │ ├── Claim: { Typerole, ValueAdmin } │ └── Claim: { Typeemail, Valuealiceexample.com } │ └── ClaimsIdentity #2 (e.g., from external provider) ├── Claim: { Typesub, Value12345 } └── Claim: { Typeidp, ValueGoogle }第三步设计序列化模型DTO理解了ClaimsPrincipal的内部结构后我们需要设计可序列化的 DTO 来承载这些数据。关键是要保留足够的信息以便在反序列化时能够完整还原原始对象。1. 设计原则在设计序列化模型时我们需要遵循以下原则1.1 完整性Completeness保留所有关键信息确保反序列化后的对象与原始对象等价Claim的所有核心属性Type、Value、ValueType、Issuer、OriginalIssuer、PropertiesClaimsIdentity的认证上下文AuthenticationType、NameClaimType、RoleClaimTypeClaimsPrincipal的多身份支持Identities 集合1.2 可序列化性Serializability使用简单类型string、int、bool 等和标准集合List、Dictionary避免接口类型、只读属性、私有构造函数等不友好的序列化特性兼容主流序列化器System.Text.Json、Newtonsoft.Json1.3 可读性Readability生成的 JSON 应当清晰易读方便调试和日志记录{ identities: [ { authenticationType: Bearer, nameClaimType: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name, roleClaimType: http://schemas.microsoft.com/ws/2008/06/identity/claims/role, claims: [ { type: name, value: Alice, valueType: http://www.w3.org/2001/XMLSchema#string, issuer: LOCAL AUTHORITY } ] } ] }2. DTO 类设计2.1 ClaimDtopublic class ClaimDto { /// summary /// 声明类型如 name、role、email /// /summary public string Type { get; set; } string.Empty; /// summary /// 声明值 /// /summary public string Value { get; set; } string.Empty; /// summary /// 值的数据类型默认为 XMLSchema#string /// /summary public string ValueType { get; set; } ClaimValueTypes.String; /// summary /// 声明颁发者 /// /summary public string Issuer { get; set; } ClaimsIdentity.DefaultIssuer; /// summary /// 原始颁发者联合身份场景 /// /summary public string OriginalIssuer { get; set; } ClaimsIdentity.DefaultIssuer; /// summary /// 自定义属性集合 /// /summary public Dictionarystring, string? Properties { get; set; } /// summary /// 从 Claim 创建 DTO /// /summary public static ClaimDto FromClaim(Claim claim) { var dto new ClaimDto { Type claim.Type, Value claim.Value, ValueType claim.ValueType, Issuer claim.Issuer, OriginalIssuer claim.OriginalIssuer }; // 仅在有自定义属性时才序列化 if (claim.Properties.Count 0) { dto.Properties new Dictionarystring, string(claim.Properties); } return dto; } /// summary /// 转换为 Claim 对象 /// /summary public Claim ToClaim() { var claim new Claim(Type, Value, ValueType, Issuer, OriginalIssuer); // 还原自定义属性 if (Properties ! null) { foreach (var prop in Properties) { claim.Properties[prop.Key] prop.Value; } } return claim; } }2.2 ClaimsIdentityDtopublic class ClaimsIdentityDto { /// summary /// 认证类型如 Bearer、Cookies /// /summary public string? AuthenticationType { get; set; } /// summary /// 用于标识用户名的声明类型 /// /summary public string NameClaimType { get; set; } ClaimsIdentity.DefaultNameClaimType; /// summary /// 用于标识角色的声明类型 /// /summary public string RoleClaimType { get; set; } ClaimsIdentity.DefaultRoleClaimType; /// summary /// 声明集合 /// /summary public ListClaimDto Claims { get; set; } new(); /// summary /// 从 ClaimsIdentity 创建 DTO /// /summary public static ClaimsIdentityDto FromClaimsIdentity(ClaimsIdentity identity) { return new ClaimsIdentityDto { AuthenticationType identity.AuthenticationType, NameClaimType identity.NameClaimType, RoleClaimType identity.RoleClaimType, Claims identity.Claims.Select(ClaimDto.FromClaim).ToList() }; } /// summary /// 转换为 ClaimsIdentity 对象 /// /summary public ClaimsIdentity ToClaimsIdentity() { var claims Claims.Select(c c.ToClaim()).ToList(); return new ClaimsIdentity( claims, AuthenticationType, NameClaimType, RoleClaimType ); } }2.3 ClaimsPrincipalDtopublic class ClaimsPrincipalDto { /// summary /// 身份集合 /// /summary public ListClaimsIdentityDto Identities { get; set; } new(); /// summary /// 从 ClaimsPrincipal 创建 DTO /// /summary public static ClaimsPrincipalDto FromClaimsPrincipal(ClaimsPrincipal principal) { return new ClaimsPrincipalDto { Identities principal.Identities .Select(ClaimsIdentityDto.FromClaimsIdentity) .ToList() }; } /// summary /// 转换为 ClaimsPrincipal 对象 /// /summary public ClaimsPrincipal ToClaimsPrincipal() { var identities Identities.Select(i i.ToClaimsIdentity()).ToList(); return new ClaimsPrincipal(identities); } }3. 使用示例3.1 序列化using System.Text.Json; // 获取当前用户的 ClaimsPrincipal var principal httpContext.User; // 转换为 DTO var dto ClaimsPrincipalDto.FromClaimsPrincipal(principal); // 序列化为 JSON var json JsonSerializer.Serialize(dto, new JsonSerializerOptions { WriteIndented true, // 格式化输出 DefaultIgnoreCondition JsonIgnoreCondition.WhenWritingNull // 忽略 null 值 }); // 记录到日志或存储 logger.LogInformation(User principal: {Principal}, json);3.2 反序列化// 从 JSON 还原 var dto JsonSerializer.DeserializeClaimsPrincipalDto(json); // 转换为 ClaimsPrincipal var principal dto?.ToClaimsPrincipal(); // 使用还原的对象 if (principal ! null) { httpContext.User principal; // 或者传递给其他服务 }3.3 实际应用场景场景 1分布式会话存储// 将用户身份序列化后存储到 Redis public async Task SaveUserSession(string sessionId, ClaimsPrincipal principal) { var dto ClaimsPrincipalDto.FromClaimsPrincipal(principal); var json JsonSerializer.Serialize(dto); await redis.StringSetAsync($session:{sessionId}, json, TimeSpan.FromHours(1)); } // 从 Redis 还原用户身份 public async TaskClaimsPrincipal? LoadUserSession(string sessionId) { var json await redis.StringGetAsync($session:{sessionId}); if (json.IsNullOrEmpty) return null; var dto JsonSerializer.DeserializeClaimsPrincipalDto(json); return dto?.ToClaimsPrincipal(); }场景 2微服务间传递身份// 在 HTTP 请求头中传递用户身份 public async TaskHttpResponseMessage CallDownstreamService(ClaimsPrincipal principal) { var dto ClaimsPrincipalDto.FromClaimsPrincipal(principal); var json JsonSerializer.Serialize(dto); var request new HttpRequestMessage(HttpMethod.Get, https://api.example.com/data); request.Headers.Add(X-User-Principal, Convert.ToBase64String(Encoding.UTF8.GetBytes(json))); return await httpClient.SendAsync(request); }场景 3审计日志// 记录用户操作时保存完整的身份信息 public void LogUserAction(ClaimsPrincipal principal, string action, object data) { var dto ClaimsPrincipalDto.FromClaimsPrincipal(principal); var auditLog new { Timestamp DateTime.UtcNow, Principal dto, Action action, Data data }; var json JsonSerializer.Serialize(auditLog, new JsonSerializerOptions { WriteIndented true }); File.AppendAllText(audit.log, json Environment.NewLine); }4. 性能优化建议4.1 按需序列化如果只需要部分信息如用户名和角色可以创建简化版的 DTOpublic class SimplePrincipalDto { public string? Name { get; set; } public Liststring Roles { get; set; } new(); public static SimplePrincipalDto FromClaimsPrincipal(ClaimsPrincipal principal) { return new SimplePrincipalDto { Name principal.Identity?.Name, Roles principal.Claims .Where(c c.Type ClaimTypes.Role) .Select(c c.Value) .ToList() }; } }4.2 缓存序列化结果对于频繁访问的用户身份信息可以缓存序列化结果private readonly ConcurrentDictionarystring, string _cache new(); public string GetCachedJson(ClaimsPrincipal principal) { var key principal.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty; return _cache.GetOrAdd(key, _ { var dto ClaimsPrincipalDto.FromClaimsPrincipal(principal); return JsonSerializer.Serialize(dto); }); }4.3 避免序列化敏感信息在序列化前过滤敏感声明public static ClaimsPrincipalDto FromClaimsPrincipalSafe(ClaimsPrincipal principal) { // 定义敏感声明类型 var sensitiveTypes new HashSetstring { password, secret, token }; var dto new ClaimsPrincipalDto { Identities principal.Identities.Select(identity new ClaimsIdentityDto { AuthenticationType identity.AuthenticationType, NameClaimType identity.NameClaimType, RoleClaimType identity.RoleClaimType, Claims identity.Claims .Where(c !sensitiveTypes.Contains(c.Type.ToLowerInvariant())) .Select(ClaimDto.FromClaim) .ToList() }).ToList() }; return dto; }第四步通过自定义 Converter 进行序列化和反序列化上一步我们使用 DTO 模式实现了序列化需要手动转换对象。如果你希望更透明地序列化ClaimsPrincipal可以使用System.Text.Json的自定义转换器JsonConverter。这样可以直接对原始对象进行序列化无需中间的 DTO 层。1. 自定义 Converter 的优势1.1 透明性直接序列化原始类型无需手动转换// DTO 方式需要显式转换 var dto ClaimsPrincipalDto.FromClaimsPrincipal(principal); var json JsonSerializer.Serialize(dto); // Converter 方式直接序列化 var json JsonSerializer.Serialize(principal, options);1.2 集中化序列化逻辑集中在 Converter 中使用时只需配置一次var options new JsonSerializerOptions(); options.Converters.Add(new ClaimsPrincipalConverter()); // 全局使用无需每次转换1.3 类型安全反序列化直接返回目标类型无需额外转换var principal JsonSerializer.DeserializeClaimsPrincipal(json, options); // 直接得到 ClaimsPrincipal不是 DTO2. 实现自定义 Converter2.1 ClaimConverterusing System.Security.Claims; using System.Text.Json; using System.Text.Json.Serialization; public class ClaimConverter : JsonConverterClaim { public override Claim? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType ! JsonTokenType.StartObject) { throw new JsonException(Expected StartObject token); } string? type null; string? value null; string? valueType ClaimValueTypes.String; string? issuer ClaimsIdentity.DefaultIssuer; string? originalIssuer ClaimsIdentity.DefaultIssuer; Dictionarystring, string? properties null; while (reader.Read()) { if (reader.TokenType JsonTokenType.EndObject) { break; } if (reader.TokenType ! JsonTokenType.PropertyName) { throw new JsonException(Expected PropertyName token); } string propertyName reader.GetString()!; reader.Read(); switch (propertyName.ToLowerInvariant()) { case type: type reader.GetString(); break; case value: value reader.GetString(); break; case valuetype: valueType reader.GetString() ?? ClaimValueTypes.String; break; case issuer: issuer reader.GetString() ?? ClaimsIdentity.DefaultIssuer; break; case originalissuer: originalIssuer reader.GetString() ?? ClaimsIdentity.DefaultIssuer; break; case properties: properties JsonSerializer.DeserializeDictionarystring, string(ref reader, options); break; default: reader.Skip(); break; } } if (string.IsNullOrEmpty(type) || value null) { throw new JsonException(Claim must have Type and Value); } var claim new Claim(type, value, valueType, issuer, originalIssuer); if (properties ! null) { foreach (var prop in properties) { claim.Properties[prop.Key] prop.Value; } } return claim; } public override void Write(Utf8JsonWriter writer, Claim value, JsonSerializerOptions options) { writer.WriteStartObject(); writer.WriteString(type, value.Type); writer.WriteString(value, value.Value); writer.WriteString(valueType, value.ValueType); writer.WriteString(issuer, value.Issuer); writer.WriteString(originalIssuer, value.OriginalIssuer); if (value.Properties.Count 0) { writer.WritePropertyName(properties); JsonSerializer.Serialize(writer, value.Properties, options); } writer.WriteEndObject(); } }2.2 ClaimsIdentityConverterpublic class ClaimsIdentityConverter : JsonConverterClaimsIdentity { public override ClaimsIdentity? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType ! JsonTokenType.StartObject) { throw new JsonException(Expected StartObject token); } string? authenticationType null; string nameClaimType ClaimsIdentity.DefaultNameClaimType; string roleClaimType ClaimsIdentity.DefaultRoleClaimType; ListClaim? claims null; while (reader.Read()) { if (reader.TokenType JsonTokenType.EndObject) { break; } if (reader.TokenType ! JsonTokenType.PropertyName) { throw new JsonException(Expected PropertyName token); } string propertyName reader.GetString()!; reader.Read(); switch (propertyName.ToLowerInvariant()) { case authenticationtype: authenticationType reader.GetString(); break; case nameclaimtype: nameClaimType reader.GetString() ?? ClaimsIdentity.DefaultNameClaimType; break; case roleclaimtype: roleClaimType reader.GetString() ?? ClaimsIdentity.DefaultRoleClaimType; break; case claims: claims JsonSerializer.DeserializeListClaim(ref reader, options); break; default: reader.Skip(); break; } } return new ClaimsIdentity( claims ?? new ListClaim(), authenticationType, nameClaimType, roleClaimType ); } public override void Write(Utf8JsonWriter writer, ClaimsIdentity value, JsonSerializerOptions options) { writer.WriteStartObject(); writer.WriteString(authenticationType, value.AuthenticationType); writer.WriteString(nameClaimType, value.NameClaimType); writer.WriteString(roleClaimType, value.RoleClaimType); writer.WritePropertyName(claims); JsonSerializer.Serialize(writer, value.Claims.ToList(), options); writer.WriteEndObject(); } }2.3 ClaimsPrincipalConverterpublic class ClaimsPrincipalConverter : JsonConverterClaimsPrincipal { public override ClaimsPrincipal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType ! JsonTokenType.StartObject) { throw new JsonException(Expected StartObject token); } ListClaimsIdentity? identities null; while (reader.Read()) { if (reader.TokenType JsonTokenType.EndObject) { break; } if (reader.TokenType ! JsonTokenType.PropertyName) { throw new JsonException(Expected PropertyName token); } string propertyName reader.GetString()!; reader.Read(); if (propertyName.Equals(identities, StringComparison.OrdinalIgnoreCase)) { identities JsonSerializer.DeserializeListClaimsIdentity(ref reader, options); } else { reader.Skip(); } } return new ClaimsPrincipal(identities ?? new ListClaimsIdentity()); } public override void Write(Utf8JsonWriter writer, ClaimsPrincipal value, JsonSerializerOptions options) { writer.WriteStartObject(); writer.WritePropertyName(identities); JsonSerializer.Serialize(writer, value.Identities.ToList(), options); writer.WriteEndObject(); } }3. 使用自定义 Converter3.1 基本使用// 配置序列化选项 var options new JsonSerializerOptions { WriteIndented true, DefaultIgnoreCondition JsonIgnoreCondition.WhenWritingNull, Converters { new ClaimConverter(), new ClaimsIdentityConverter(), new ClaimsPrincipalConverter() } }; // 序列化 var principal httpContext.User; var json JsonSerializer.Serialize(principal, options); // 反序列化 var deserializedPrincipal JsonSerializer.DeserializeClaimsPrincipal(json, options);3.2 全局配置在 ASP.NET Core 中可以全局配置序列化选项// Program.cs 或 Startup.cs builder.Services.ConfigureJsonOptions(options { options.JsonSerializerOptions.Converters.Add(new ClaimConverter()); options.JsonSerializerOptions.Converters.Add(new ClaimsIdentityConverter()); options.JsonSerializerOptions.Converters.Add(new ClaimsPrincipalConverter()); });3.3 创建扩展方法为了更方便使用可以创建扩展方法public static class ClaimsPrincipalExtensions { private static readonly JsonSerializerOptions DefaultOptions new() { WriteIndented true, DefaultIgnoreCondition JsonIgnoreCondition.WhenWritingNull, Converters { new ClaimConverter(), new ClaimsIdentityConverter(), new ClaimsPrincipalConverter() } }; /// summary /// 将 ClaimsPrincipal 序列化为 JSON /// /summary public static string ToJson(this ClaimsPrincipal principal, JsonSerializerOptions? options null) { return JsonSerializer.Serialize(principal, options ?? DefaultOptions); } /// summary /// 从 JSON 反序列化为 ClaimsPrincipal /// /summary public static ClaimsPrincipal? FromJson(string json, JsonSerializerOptions? options null) { return JsonSerializer.DeserializeClaimsPrincipal(json, options ?? DefaultOptions); } /// summary /// 将 ClaimsPrincipal 安全地序列化为 JSON过滤敏感信息 /// /summary public static string ToJsonSafe(this ClaimsPrincipal principal, HashSetstring? sensitiveTypes null) { sensitiveTypes ?? new HashSetstring(StringComparer.OrdinalIgnoreCase) { password, secret, token, apikey }; // 创建过滤后的副本 var filteredIdentities principal.Identities.Select(identity { var filteredClaims identity.Claims .Where(c !sensitiveTypes.Contains(c.Type)) .ToList(); return new ClaimsIdentity( filteredClaims, identity.AuthenticationType, identity.NameClaimType, identity.RoleClaimType ); }).ToList(); var filteredPrincipal new ClaimsPrincipal(filteredIdentities); return filteredPrincipal.ToJson(); } }使用扩展方法// 序列化 var json httpContext.User.ToJson(); // 安全序列化自动过滤敏感信息 var safeJson httpContext.User.ToJsonSafe(); // 反序列化 var principal ClaimsPrincipalExtensions.FromJson(json);4. 实际应用示例4.1 中间件中传递用户身份public class UserPrincipalMiddleware { private readonly RequestDelegate _next; public UserPrincipalMiddleware(RequestDelegate next) { _next next; } public async Task InvokeAsync(HttpContext context) { // 如果请求头包含序列化的用户身份则还原 if (context.Request.Headers.TryGetValue(X-User-Principal, out var principalHeader)) { try { var json Encoding.UTF8.GetString(Convert.FromBase64String(principalHeader!)); var principal ClaimsPrincipalExtensions.FromJson(json); if (principal ! null) { context.User principal; } } catch (Exception ex) { // 记录错误但不中断请求 Console.WriteLine($Failed to deserialize principal: {ex.Message}); } } await _next(context); } }4.2 分布式缓存public class UserSessionService { private readonly IDistributedCache _cache; public UserSessionService(IDistributedCache cache) { _cache cache; } public async Task SaveSessionAsync(string sessionId, ClaimsPrincipal principal) { var json principal.ToJson(); var bytes Encoding.UTF8.GetBytes(json); await _cache.SetAsync(sessionId, bytes, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow TimeSpan.FromHours(1) }); } public async TaskClaimsPrincipal? GetSessionAsync(string sessionId) { var bytes await _cache.GetAsync(sessionId); if (bytes null) return null; var json Encoding.UTF8.GetString(bytes); return ClaimsPrincipalExtensions.FromJson(json); } }5. DTO vs Converter如何选择5.1 使用 DTO 的场景优点显式转换逻辑清晰可以灵活定制传输的数据结构更容易进行数据验证和转换不依赖特定的序列化框架适用场景API 数据传输需要版本控制和向后兼容跨语言/跨平台通信需要数据脱敏或转换的场景长期存储的数据格式示例// 为前端提供简化的用户信息 public class UserInfoDto { public string UserId { get; set; } public string Name { get; set; } public Liststring Roles { get; set; } public static UserInfoDto FromPrincipal(ClaimsPrincipal principal) { return new UserInfoDto { UserId principal.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? , Name principal.Identity?.Name ?? , Roles principal.Claims .Where(c c.Type ClaimTypes.Role) .Select(c c.Value) .ToList() }; } }5.2 使用 Converter 的场景优点透明序列化使用更简洁保持原始类型无需转换全局配置一次处处可用更好地支持嵌套对象序列化适用场景内部服务通信同技术栈临时缓存和会话存储调试和日志记录需要完整保留对象状态示例// 缓存完整的用户身份 public async Task CacheUserPrincipal(string key, ClaimsPrincipal principal) { // 直接序列化无需转换 var json principal.ToJson(); await _cache.SetStringAsync(key, json, TimeSpan.FromMinutes(30)); }5.3 混合使用在实际项目中两种方式可以共存public class UserService { // 对外 API使用 DTO public UserInfoDto GetPublicUserInfo(ClaimsPrincipal principal) { return UserInfoDto.FromPrincipal(principal); } // 内部缓存使用 Converter public async Task CacheUserSession(string sessionId, ClaimsPrincipal principal) { var json principal.ToJson(); await SaveToCache(sessionId, json); } // 审计日志使用 Converter安全模式 public void LogUserAction(ClaimsPrincipal principal, string action) { var json principal.ToJsonSafe(); _logger.LogInformation(Action: {Action}, User: {User}, action, json); } }6. 性能对比using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; [MemoryDiagnoser] public class SerializationBenchmark { private ClaimsPrincipal _principal; private JsonSerializerOptions _options; [GlobalSetup] public void Setup() { var claims new[] { new Claim(ClaimTypes.Name, Alice), new Claim(ClaimTypes.Email, aliceexample.com), new Claim(ClaimTypes.Role, Admin), new Claim(ClaimTypes.Role, User) }; var identity new ClaimsIdentity(claims, Bearer); _principal new ClaimsPrincipal(identity); _options new JsonSerializerOptions { Converters { new ClaimConverter(), new ClaimsIdentityConverter(), new ClaimsPrincipalConverter() } }; } [Benchmark] public string SerializeWithDto() { var dto ClaimsPrincipalDto.FromClaimsPrincipal(_principal); return JsonSerializer.Serialize(dto); } [Benchmark] public string SerializeWithConverter() { return JsonSerializer.Serialize(_principal, _options); } [Benchmark] public ClaimsPrincipal DeserializeWithDto() { var dto ClaimsPrincipalDto.FromClaimsPrincipal(_principal); var json JsonSerializer.Serialize(dto); var deserializedDto JsonSerializer.DeserializeClaimsPrincipalDto(json); return deserializedDto!.ToClaimsPrincipal(); } [Benchmark] public ClaimsPrincipal DeserializeWithConverter() { var json JsonSerializer.Serialize(_principal, _options); return JsonSerializer.DeserializeClaimsPrincipal(json, _options)!; } } // 运行基准测试 // BenchmarkRunner.RunSerializationBenchmark();预期结果Converter 方式通常略快少一次对象转换内存使用相近两者性能差异在大多数场景下可忽略7. 注意事项7.1 空值处理确保 Converter 正确处理 null 值public override void Write(Utf8JsonWriter writer, ClaimsPrincipal? value, JsonSerializerOptions options) { if (value null) { writer.WriteNullValue(); return; } // ... 正常序列化逻辑 }7.2 循环引用虽然 ClaimsPrincipal 结构简单不会出现循环引用但在扩展时需注意var options new JsonSerializerOptions { ReferenceHandler ReferenceHandler.IgnoreCycles, // 处理循环引用 Converters { /* ... */ } };7.3 版本兼容性序列化格式应考虑向后兼容public override ClaimsIdentity? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 为新增字段提供默认值 string nameClaimType ClaimsIdentity.DefaultNameClaimType; // 兼容旧版本 JSON可能缺少某些字段 // ... }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2482824.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…