1. 何为后端数据脱敏?
在实际业务系统中,接口往往会返回包含敏感信息的数据。如果这些数据直接明文返回给前端或第三方系统,存在一定的安全与合规风险。
数据脱敏(Data Masking),指的是:在不修改原始数据的前提下,对返回给客户端的数据进行遮挡或模糊处理,以防止敏感信息被完整展示。真实的业务场景中,数据脱敏后的展示效果图如下。其中红色框框住的,就是脱敏后的信息。
常见的需要脱敏的字段:手机号、身份证号、邮箱、姓名(如支付宝)、银行卡号、薪资信息等。是否脱敏,取决于:接口使用场景、用户角色、数据敏感级别、系统需求需要。
脱敏的核心目标是:数据可用、数据真实、你有权限查看、但不可全看。
2. 脱敏 ≠ 加密 ≠ 删除
这是很多初学者容易混淆的地方,你需要记住的是,脱敏只发生在数据返回阶段,数据库中仍然保存完整数据,二者互不干扰。我们用一个表格来展示脱敏/加密/删除的区别:
| 对比项 | 脱敏 | 加密 | 删除 |
|---|---|---|---|
| 是否可还原 | 否 | 是 | 否 |
| 是否修改原数据 | 否 | 是 | 是 |
| 主要用途 | 展示安全 | 存储/传输安全 | 数据清理 |
3. 为什么要在后端做脱敏?
有些项目会尝试在前端做脱敏处理,但这并不安全,原因包括:
- 接口返回的数据本身仍是明文
- 抓包、日志、调试工具都能看到完整数据
- 权限控制难以统一
正确做法:脱敏逻辑必须在后端完成。
4.后端脱敏实现方式
1️⃣ VO 层手动脱敏(简单场景)
适合字段少、接口简单的项目。比如针对手机号的脱敏代码实现:
public String maskPhone(String phone) { if (phone == null || phone.length() < 7) { return phone; } return phone.substring(0, 3) + "****" + phone.substring(7); }优点:实现简单、通俗易懂
缺点:重复代码多、不利于统一规范
2️⃣ Jackson 自定义序列化(推荐)
我们可以通过 Jackson 的 JsonSerialize,在序列化阶段统一脱敏。比如同样是针对手机号进行的脱敏代码编写:
public class PhoneMaskSerializer extends JsonSerializer<String> { @Override public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { if (value == null) { gen.writeNull(); return; } gen.writeString(value.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")); } }之后我们在字段使用:
@JsonSerialize(using = PhoneMaskSerializer.class) private String phone;优点:与业务逻辑无关、前端无感知。
3️⃣ 注解 + AOP(企业级推荐方案)
适合字段多、接口多的中大型项目。
首先我们需要定义脱敏注解:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Desensitize { DesensitizeType type(); }其中待脱敏字段有:
public enum DesensitizeType { PHONE, EMAIL, ID_CARD }AOP 统一处理返回结果:
@Aspect @Component public class DesensitizeAspect { @Around("execution(* com.xxx.controller..*(..))") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { Object result = joinPoint.proceed(); handle(result); return result; } private void handle(Object obj) throws IllegalAccessException { if (obj == null) return; if (obj instanceof Collection<?> list) { for (Object item : list) { handle(item); } return; } for (Field field : obj.getClass().getDeclaredFields()) { if (!field.isAnnotationPresent(Desensitize.class)) { continue; } field.setAccessible(true); Object value = field.get(obj); if (value instanceof String str) { DesensitizeType type = field.getAnnotation(Desensitize.class).type(); field.set(obj, mask(str, type)); } } } }手机号码字段使用示例:
@Desensitize(type = DesensitizeType.PHONE) private String phone;5. 脱敏设计中的注意事项
不要在 Entity 层脱敏
Entity 是原始数据模型,不应被污染。不要修改数据库中的值
脱敏只用于展示。注意 List / Page / Tree 结构的递归处理
权限高的用户可以不脱敏
可与权限系统结合控制。
总结一下:数据脱敏是后端数据安全设计的基础能力,它≠ 加密,二者职责不同。推荐使用 注解 + AOP 实现统一、可维护的脱敏方案,并将脱敏逻辑放在接口返回阶段。
你,学废了吗?