java中需脱敏的字段包括password、idcard、bankcard、phone、email等明确敏感字段,以及字段名或值匹配“pwd”“phone”等模式的字段;脱敏须在日志写入前完成,推荐通过logback自定义converter或json序列化层实现字段级过滤,避免误脱、漏脱及副作用。

Java中哪些字段需要脱敏?先看日志里真正暴露的风险点
日志里直接打印 password、idCard、bankCard、phone、email 这类字段,是敏感数据泄露的高频入口。不是所有字符串都要模糊处理,比如 username 或 orderNo 通常可明文;但只要字段名或值符合常见敏感模式(如 11 位数字+连续出现“phone”,或含“pwd”“pass”“token”等子串),就得干预。
- 脱敏逻辑必须在日志写入前完成,不能依赖 Logback 的
%replace做正则替换——它只处理最终字符串,无法识别字段结构,容易误伤或漏脱 - 不建议在业务代码里每处
log.info("user: {}", user)都手动调用脱敏工具,维护成本高且易遗漏 - 最小侵入方式:统一拦截日志参数,在
Object.toString()或 JSON 序列化环节做字段级过滤
用Logback + 自定义Converter实现字段级脱敏
Logback 的 PatternLayout 支持自定义 Converter,能拿到原始日志参数对象(ILoggingEvent.getArgumentArray()),比纯文本替换更精准。
- 继承
ClassicConverter,重写convert()方法,对每个参数递归检查是否为 Map / DTO / List,并按预设规则脱敏字段 - 规则配置建议外置为 JSON 文件(如
log-sensitivedata.json),内容示例:{ "User": ["password", "idCard"], "OrderRequest": ["payToken", "cardNo"] } - 注意
convert()中不要触发对象的 getter 方法副作用(如 Hibernate 懒加载引发 N+1 查询),优先用反射读取field.get(obj),跳过代理对象或 null 值
JSON序列化时脱敏比日志拦截更可靠?要看你用的是哪个库
如果项目已用 fastjson 或 jackson 输出结构化日志(如 ELK 场景),直接在序列化层控制更稳:
-
fastjson:用@JSONField(serialize = false)或全局SerializeConfig.addFilter(),但注意addFilter()对泛型集合支持弱,List<user></user>里的User字段可能被跳过 -
jackson:推荐@JsonInclude(JsonInclude.Include.NON_EMPTY)+ 自定义SimpleBeanPropertyFilter,配合FilterProvider动态绑定,能区分不同接口的日志级别(如支付接口全脱敏,查询接口仅脱敏idCard) - 别在
toString()里写脱敏逻辑——很多框架(如 Spring Boot Actuator)会调用它生成诊断信息,导致非日志场景也误脱敏
脱敏后日志还能查问题吗?保留可逆标识是关键
完全替换成 <strong>*</strong> 会让排查变得困难。比如 phone 脱敏成 138***<em>1234</em> 是合理方案,但 token 若只留前缀,需确保前缀足够区分(如 tkn_abc... → tkn_abc),否则多个请求日志看起来一模一样。
立即学习“Java免费学习笔记(深入)”;
- 避免使用哈希或加密——日志系统不带密钥,无法还原,也失去可读性
- 对数据库主键类字段(如
userId),可改用maskId(userId)做固定长度混淆(如 123456 → 876543),既不可逆又保留分布特征 - 所有脱敏函数必须线程安全,禁止用
SimpleDateFormat或静态StringBuilder,曾有线上事故因脱敏器内部缓存导致日志内容错乱
脱敏不是加个星号就完事,真正难的是在不破坏日志可读性、不影响性能、不引入新 bug 的前提下,让敏感字段在该消失的地方消失,该露头的地方还能认出来。










