Appearance
Spring 中的数据脱敏与敏感信息保护
很多项目一提到“安全”,很容易先想到加密。但真实企业开发里,还有另一条非常常见、也非常容易被忽视的主线:数据脱敏。
这篇文章重点讲:
- 数据脱敏到底是什么
- 它和加密、哈希有什么区别
- 为什么脱敏在日志、接口返回、后台页面里特别重要
- Spring 项目里常见的脱敏落地方式有哪些
- 工程上最容易踩的坑是什么
1. 数据脱敏到底是什么
数据脱敏 可以看成:在不暴露完整敏感信息的前提下,只展示或输出必要的一部分内容。
例如:
- 手机号
13812345678显示成138****5678 - 身份证号只显示前 6 位和后 4 位
- 银行卡号只显示最后 4 位
- 姓名显示成
张*
它解决的核心问题是:很多场景并不需要看到完整明文,但如果系统直接把明文吐出去,泄漏风险会非常高。
2. 脱敏和加密、哈希有什么区别
这几个词经常混在一起,但它们解决的问题不同。
2.1 脱敏
重点是控制展示内容,减少泄漏面。
2.2 加密
重点是让别人看不懂原始内容。
2.3 哈希
重点是把原始内容映射成不可逆摘要。
可以直接这样区分:
- 脱敏是“少给别人看”
- 加密是“就算拿到了也看不懂”
- 哈希是“通常不打算再还原回原文”
要注意的是:脱敏不替代加密,加密也不替代脱敏。
很多场景里,这两件事反而要同时存在。
3. 为什么数据脱敏在企业项目里特别重要
很多敏感数据泄漏,不一定是数据库被拖走那么严重的事故。
更常见的问题其实来自:
- 后台页面把完整手机号直接展示给了不该看到的人
- 接口响应把完整身份证号返回到了前端
- 日志打印把 token、手机号、密码字段打出来了
- 测试环境、工单系统、排障截图里带出了真实敏感信息
所以数据脱敏解决的不是“黑客攻防”这一层,而是系统内部日常开发、运维、测试、客服、分析等链路里的过度暴露问题。
4. 哪些数据通常应该优先考虑脱敏
企业开发里,下面这些字段通常都很值得优先纳入脱敏范围:
- 手机号
- 身份证号
- 银行卡号
- 邮箱
- 姓名
- 地址
- token
- access key、secret key
- 密码及支付口令
这里要特别注意:密码这类字段更常见的正确做法不是“脱敏显示”,而是根本不应该以明文方式出现。
5. Spring 项目里最常见的脱敏场景
如果按工程位置来拆,最常见的通常有 4 类:
- 页面展示脱敏
- 接口返回脱敏
- 日志输出脱敏
- 导出文件脱敏
5.1 页面展示脱敏
例如后台管理系统里,客服查看用户资料时,很多时候并不需要看到完整身份证号或银行卡号。
这时更合理的做法通常是:默认只展示脱敏值,只有极少数高权限操作才允许看原文。
5.2 接口返回脱敏
有些接口给前端返回用户信息时,如果把完整手机号、证件号直接透出,前端日志、浏览器缓存、抓包链路都可能扩大暴露面。
5.3 日志输出脱敏
这是最容易被忽视的一类。
很多线上敏感信息泄漏,不是因为数据库被攻破,而是因为日志打印太“诚实”了。
5.4 导出文件脱敏
报表导出、Excel 导出、对账单下载这些场景,也经常需要脱敏。
因为文件一旦被转发,传播范围往往比系统内页面更难控制。
6. 一个最简单的脱敏工具示例
看最容易理解的一层:工具方法脱敏。
例如手机号脱敏:
java
public final class MaskingUtils {
private MaskingUtils() {
}
public static String maskPhone(String phone) {
if (phone == null || phone.length() < 7) {
return phone;
}
return phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4);
}
public static String maskEmail(String email) {
if (email == null || !email.contains("@")) {
return email;
}
int index = email.indexOf('@');
if (index <= 1) {
return "***" + email.substring(index);
}
return email.substring(0, 1) + "***" + email.substring(index);
}
}在返回对象组装阶段使用:
java
public class UserVO {
private Long id;
private String phone;
private String email;
public static UserVO from(User user) {
UserVO vo = new UserVO();
vo.id = user.getId();
vo.phone = MaskingUtils.maskPhone(user.getPhone());
vo.email = MaskingUtils.maskEmail(user.getEmail());
return vo;
}
}这个例子虽然简单,但已经表达了一个很重要的工程习惯:脱敏最好发生在“对外输出”这一层,而不是等前端自己决定怎么遮。
7. 如果想做得更统一,Spring 里怎么落地
企业项目里,如果每个接口都手写一次脱敏,后面通常会越来越乱。
更稳妥的思路通常有几种:
- 在 DTO / VO 转换层统一处理
- 用 Jackson 自定义序列化器处理
- 用 AOP 或响应包装层做统一脱敏
- 日志框架层做敏感字段过滤
7.1 在 DTO / VO 层统一处理
这是最直接、也最容易维护的一种方式。
它的好处是:
- 脱敏边界清楚
- 业务语义明确
- 不容易误伤内部处理逻辑
缺点是:如果接口很多、字段很多,容易写重复代码。
7.2 用 Jackson 自定义序列化器
如果希望对象在转 JSON 时自动脱敏,可以在 Spring MVC 的响应序列化阶段处理。
例如自定义一个手机号脱敏序列化器:
java
public class PhoneMaskSerializer extends JsonSerializer<String> {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(MaskingUtils.maskPhone(value));
}
}然后在字段上使用:
java
public class UserVO {
private Long id;
@JsonSerialize(using = PhoneMaskSerializer.class)
private String phone;
}这类方案的优势是 脱敏逻辑和输出过程绑定得更紧。
但也要注意边界 不是所有场景都适合一序列化就统一脱敏,否则有时会影响内部接口或管理端真实需求。
7.3 日志框架层脱敏
对日志来说,更常见的做法不是业务代码里到处手写:log.info("phone={}", MaskingUtils.maskPhone(phone));
而是:
- 在日志切面里统一处理
- 在日志框架里做正则替换
- 在请求日志拦截器里屏蔽指定字段
因为日志问题的本质是:不该把敏感字段明文打出去。
如果每个人都靠自觉,很难长期稳定。
8. 一个自定义脱敏注解示意
如果想把脱敏表达得更统一,也可以做一个简单的自定义注解。
例如:
java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MaskPhone {
}然后再配合统一序列化逻辑或切面处理。
这里要注意的是:注解本身只负责表达“这个字段需要脱敏”,真正让它生效的,仍然是后续处理逻辑。
这和 Spring 里其他很多自定义注解的主线是一致的。
9. 数据脱敏不等于权限控制
这是非常容易被忽略的边界。
有些团队会觉得:字段已经脱敏了,就算安全控制做完了。
其实不是。
更具体一点:
- 脱敏解决的是“展示时少暴露”
- 权限控制解决的是“谁可以访问、谁不可以访问”
- 审计解决的是“谁看过、谁导出过、谁操作过”
所以真正稳妥的做法通常是:脱敏 + 权限控制 + 审计
而不是只做其中一层。
10. 工程上最容易踩的坑
10.1 误区一:数据库加密了,就不需要脱敏
数据库加密解决的是存储层保护;一旦数据被正常查询出来,输出链路依然可能泄漏。
10.2 误区二:脱敏放前端做就行
如果后端已经把完整敏感信息返回给前端,暴露面其实已经扩大了。
10.3 误区三:日志是内部看的,所以可以打全量明文
真实事故里,日志往往就是最容易扩散敏感数据的地方。
10.4 误区四:所有场景都用同一种脱敏规则
不同场景对展示粒度要求不同:
- 用户自己看自己的资料
- 客服看用户资料
- 运营看统计报表
- 风控看高风险事件
这些场景的脱敏强度通常不应该完全一样。
11. 一份更实用的落地建议
如果你在 Spring 项目里真的要做敏感信息保护,下面这套习惯通常更稳:
- 对手机号、身份证号、银行卡号、邮箱等字段建立统一脱敏规则
- 默认在对外响应层做脱敏,而不是把责任丢给前端
- 日志打印默认屏蔽密码、token、证件号、手机号等字段
- 对导出、报表、截图、审计链路额外关注脱敏策略
- 高敏数据同时配合加密、权限控制和访问审计
12. 总结
把数据脱敏压缩成最核心的几句话,就是:
- 脱敏解决的是“该展示多少”,不是“数据能不能看懂”
- 脱敏不等于加密,也不等于权限控制
- Spring 项目里最常见的脱敏位置,是响应层、日志层和导出层
- 真正稳妥的敏感信息保护,通常要把脱敏、加密、权限和审计一起考虑