You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 6敏感数据通用掩码方案:自定义[SensitiveField]属性实现

通用敏感字段掩码实现方案(.NET Core 6 API)

针对你需要在API层统一处理账号、SSN等敏感字段掩码的需求,以下是基于自定义属性的通用实现方案,替代原有重复的字段处理逻辑:

1. 定义自定义[SensitiveField]属性

创建标记类,用来标识需要掩码的字段,支持自定义保留位数和掩码字符:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SensitiveFieldAttribute : Attribute
{
    // 默认保留后4位
    public int KeepLastCharacters { get; set; } = 4;
    // 默认用*作为掩码字符
    public char MaskCharacter { get; set; } = '*';
}

2. 实现通用掩码处理工具类

编写复用性掩码逻辑,统一处理字符串的掩码规则:

public static class SensitiveDataMasker
{
    public static string MaskSensitiveValue(string? value, int keepLast = 4, char maskChar = '*')
    {
        if (string.IsNullOrEmpty(value))
            return value;

        // 长度小于等于保留位数时,直接返回原内容
        if (value.Length <= keepLast)
            return value;

        var maskedPart = new string(maskChar, value.Length - keepLast);
        var visiblePart = value.Substring(value.Length - keepLast);
        return $"{maskedPart}{visiblePart}";
    }
}

3. 方案一:自动序列化时处理(推荐)

利用System.Text.Json的自定义转换器,在API返回JSON时自动根据权限掩码敏感字段,无需手动修改DTO内容:

3.1 自定义Json转换器

public class SensitiveFieldConverter : JsonConverter<string>
{
    private readonly bool _shouldMask;

    // 传入权限判断结果,控制是否启用掩码
    public SensitiveFieldConverter(bool shouldMask)
    {
        _shouldMask = shouldMask;
    }

    public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return reader.GetString();
    }

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
        if (!_shouldMask)
        {
            writer.WriteStringValue(value);
            return;
        }

        // 获取当前字段的[SensitiveField]属性配置
        var propertyInfo = options.Context.Context as PropertyInfo;
        var sensitiveAttr = propertyInfo?.GetCustomAttribute<SensitiveFieldAttribute>();

        if (sensitiveAttr != null)
        {
            var maskedValue = SensitiveDataMasker.MaskSensitiveValue(value, sensitiveAttr.KeepLastCharacters, sensitiveAttr.MaskCharacter);
            writer.WriteStringValue(maskedValue);
        }
        else
        {
            writer.WriteStringValue(value);
        }
    }
}

3.2 注册ActionFilter处理权限与转换器

public class SensitiveDataFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // 从HttpContext获取权限判断(示例:可根据Claims/业务逻辑调整)
        var noPermission = context.HttpContext.Items["NoPermission"] as bool? ?? false;

        // 配置序列化选项,注入自定义转换器
        var jsonOptions = context.HttpContext.RequestServices.GetRequiredService<IOptions<JsonOptions>>().Value;
        jsonOptions.JsonSerializerOptions.Converters.Clear();
        jsonOptions.JsonSerializerOptions.Converters.Add(new SensitiveFieldConverter(noPermission));
        
        // 传递属性上下文,用于获取字段的[SensitiveField]配置
        jsonOptions.JsonSerializerOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver
        {
            Modifiers = { typeInfo =>
                {
                    foreach (var property in typeInfo.Properties)
                    {
                        property.Context = new JsonSerializerContext { Context = property.PropertyInfo };
                    }
                }
            }
        };
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
    }
}

3.3 在Program.cs注册Filter

builder.Services.AddControllers(options =>
{
    options.Filters.Add<SensitiveDataFilter>();
});

4. 方案二:手动映射时处理(适用于非自动序列化场景)

如果需要手动控制DTO的掩码时机,可通过反射批量处理标记字段:

4.1 编写映射工具方法

public static class DtoMaskHelper
{
    public static T MaskSensitiveFields<T>(T dto, bool noPermission) where T : class
    {
        if (!noPermission || dto == null)
            return dto;

        var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (var prop in properties)
        {
            var sensitiveAttr = prop.GetCustomAttribute<SensitiveFieldAttribute>();
            if (sensitiveAttr != null && prop.PropertyType == typeof(string))
            {
                var value = prop.GetValue(dto) as string;
                var maskedValue = SensitiveDataMasker.MaskSensitiveValue(value, sensitiveAttr.KeepLastCharacters, sensitiveAttr.MaskCharacter);
                prop.SetValue(dto, maskedValue);
            }
        }
        return dto;
    }
}

4.2 替换原有业务代码

BankDetails form = new()
{
    RoutingNumber = transmissionSettings?.RoutingNumber,
    AccountNumber = transmissionSettings?.AccountNumber,
};

// 统一处理敏感字段掩码
if (NoPermission)
{
    DtoMaskHelper.MaskSensitiveFields(form, NoPermission);
}

5. DTO简化使用

现在你的DTO无需再写冗余的DisplayXXX属性,直接标记敏感字段即可:

public class BankDetails
{
    // 默认规则:保留后4位,用*掩码
    [SensitiveField]
    public string? RoutingNumber { get; set; }

    // 自定义掩码字符为#
    [SensitiveField(MaskCharacter = '#')]
    public string? AccountNumber { get; set; }
}

内容的提问来源于stack exchange,提问作者sujith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 23:02:30