如何将Claim序列化为JSON时排除Subject字段?
序列化Claim时排除Subject字段的优雅方案
我在基础Web API中使用System.Security.Claims和JWT令牌,已在program.cs中配置.AddAuthentication().AddJwtBearer()及登录方法,运行正常。现需将名为“Host”的自定义Claim(存储请求来源主机名)序列化为JSON返回给客户端。
使用的JSON序列化工具类如下:
public static class JSonHelpers { public static JsonSerializerSettings NoLoop = new() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; public static string SaveAsJson<T>(this T data, Formatting format = Formatting.Indented, JsonSerializerSettings? settings = null) { try { return JsonConvert.SerializeObject(data, format, settings); } catch (Exception ex) { return ex.Message; } } }
初始控制器方法:
[HttpGet("host")] [Authorize] [SwaggerResponse(StatusCodes.Status200OK)] [SwaggerResponse(StatusCodes.Status401Unauthorized)] [Produces(MediaTypeNames.Text.Plain)] public string HostClaim() { return User.Claims.Where(c=>c.Type=="Host").ToList().SaveAsJson(settings: JSonHelpers.NoLoop); }
直接序列化Claim数组时,返回的JSON会包含Subject字段,导致冗余数据过多。目前通过创建匿名类选择所需字段可解决问题,但写法不够优雅,想知道是否可以直接排除不需要的Subject字段。
可行方案
方案1:自定义ContractResolver全局排除Subject字段
修改JSonHelpers,添加自定义的契约解析器,专门排除Claim类的Subject属性:
public static class JSonHelpers { public static JsonSerializerSettings NoLoop = new() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = new IgnoreClaimSubjectResolver() }; public static string SaveAsJson<T>(this T data, Formatting format = Formatting.Indented, JsonSerializerSettings? settings = null) { try { return JsonConvert.SerializeObject(data, format, settings ?? NoLoop); } catch (Exception ex) { return ex.Message; } } private class IgnoreClaimSubjectResolver : DefaultContractResolver { protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); // 过滤Claim类的Subject属性 if (member.DeclaringType == typeof(Claim) && member.Name == nameof(Claim.Subject)) { property.ShouldSerialize = _ => false; } return property; } } }
修改后,原控制器方法无需改动,直接调用SaveAsJson就会自动排除Subject字段:
public string HostClaim() { return User.Claims.Where(c=>c.Type=="Host").ToList().SaveAsJson(); }
方案2:封装扩展方法简化匿名类写法
如果不想修改全局序列化设置,可以把匿名类的字段选择逻辑封装成扩展方法,让控制器代码更简洁:
public static class ClaimExtensions { public static object ToSerializableClaim(this Claim claim) { return new { claim.Issuer, claim.OriginalIssuer, claim.Properties, claim.Type, claim.Value, claim.ValueType }; } }
控制器方法即可简化为:
public string HostClaim() { return User.Claims.Where(c=>c.Type=="Host") .Select(c => c.ToSerializableClaim()) .ToList() .SaveAsJson(settings: JSonHelpers.NoLoop); }
内容的提问来源于stack exchange,提问作者Wim ten Brink
相关产品推荐
相关产品推荐

