本文目录导读:

在Java中实现数据脱敏,通常有几种常见的方法:字符串替换、注解+AOP、MyBatis插件、以及JSON序列化拦截,以下是几种主流且实用的实现方案及其代码示例。
工具类方法(最基础,手动调用)
适用于简单场景,如日志打印、手动处理单个字段。
public class DesensitizedUtil {
/**
* 手机号脱敏:保留前3后4
*/
public static String mobile(String phone) {
if (phone == null || phone.length() != 11) return phone;
return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}
/**
* 身份证号脱敏:保留前6后4
*/
public static String idCard(String id) {
if (id == null || id.length() < 10) return id;
return id.replaceAll("(\\d{6})\\d{8,12}(\\d{4})", "$1********$2");
}
/**
* 邮箱脱敏:用户名部分显示首字母+***
*/
public static String email(String email) {
if (email == null || !email.contains("@")) return email;
String name = email.substring(0, email.indexOf("@"));
String domain = email.substring(email.indexOf("@"));
return name.charAt(0) + "***" + domain;
}
/**
* 姓名脱敏:显示姓氏,名字用*代替(中文)
*/
public static String name(String name) {
if (name == null || name.length() <= 1) return name;
if (name.length() == 2) {
return name.charAt(0) + "*";
}
return name.charAt(0) + "*" + name.substring(name.length() - 1);
}
}
使用示例:
System.out.println(DesensitizedUtil.mobile("13812345678")); // 138****5678
基于注解 + AOP(推荐,侵入性低)
适合全局脱敏,尤其是返回给前端的JSON数据。
步骤:
- 定义脱敏枚举和注解
- 使用 Jackson 自定义序列化器
- 在字段上加注解
1 定义脱敏类型枚举
public enum DesensitizedType {
MOBILE, // 手机号
ID_CARD, // 身份证
EMAIL, // 邮箱
NAME // 姓名
}
2 自定义注解
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizedSerializer.class)
public @interface Sensitive {
DesensitizedType value();
}
3 自定义 Jackson 序列化器
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class DesensitizedSerializer extends JsonSerializer<String> {
private DesensitizedType type;
public DesensitizedSerializer(DesensitizedType type) {
this.type = type;
}
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
switch (type) {
case MOBILE:
gen.writeString(DesensitizedUtil.mobile(value));
break;
case ID_CARD:
gen.writeString(DesensitizedUtil.idCard(value));
break;
case EMAIL:
gen.writeString(DesensitizedUtil.email(value));
break;
case NAME:
gen.writeString(DesensitizedUtil.name(value));
break;
default:
gen.writeString(value);
}
}
}
4 在实体类中使用
public class UserDTO {
@Sensitive(DesensitizedType.NAME)
private String name;
@Sensitive(DesensitizedType.MOBILE)
private String phone;
@Sensitive(DesensitizedType.EMAIL)
private String email;
// getter/setter
}
这样,当对象通过 Jackson 序列化返回给前端时,会自动脱敏。
MyBatis 插件方式(数据库层面拦截)
适用于从数据库查询出来后自动脱敏,对业务代码完全无侵入。
1 自定义注解(用在实体字段上)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveField {
DesensitizedType value();
}
2 MyBatis 拦截器(ResultSetHandler)
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.*;
@Intercepts({
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class DesensitizationInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
List<Object> results = (List<Object>) invocation.proceed();
for (Object result : results) {
if (result != null) {
process(result);
}
}
return results;
}
private void process(Object obj) throws IllegalAccessException {
Class<?> clazz = obj.getClass();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(SensitiveField.class)) {
SensitiveField annotation = field.getAnnotation(SensitiveField.class);
field.setAccessible(true);
String value = (String) field.get(obj);
if (value != null) {
String desensitized = doDesensitize(value, annotation.value());
field.set(obj, desensitized);
}
}
}
}
private String doDesensitize(String value, DesensitizedType type) {
switch (type) {
case MOBILE: return DesensitizedUtil.mobile(value);
case ID_CARD: return DesensitizedUtil.idCard(value);
case EMAIL: return DesensitizedUtil.email(value);
case NAME: return DesensitizedUtil.name(value);
default: return value;
}
}
}
注册插件(Spring Boot + MyBatis配置):
@Configuration
public class MyBatisConfig {
@Bean
public Interceptor desensitizationInterceptor() {
return new DesensitizationInterceptor();
}
}
实体类使用:
public class User {
@SensitiveField(DesensitizedType.NAME)
private String name;
@SensitiveField(DesensitizedType.MOBILE)
private String phone;
}
日志脱敏(配合 Logback / Log4j)
使用 MessageConverter 或自定义 Layout,对日志中的敏感信息进行替换。
示例(基于正则替换,Logback)
public class SensitiveDataConverter extends MessageConverter {
@Override
public String convert(ILoggingEvent event) {
String msg = event.getFormattedMessage();
// 手机号:3-4-4格式
msg = msg.replaceAll("(1[3-9]\\d)\\d{4}(\\d{4})", "$1****$2");
// 身份证:18位
msg = msg.replaceAll("(\\d{6})\\d{8,12}(\\d{4})", "$1********$2");
return msg;
}
}
logback.xml配置:
<conversionRule conversionWord="msg" converterClass="com.example.SensitiveDataConverter"/>
如何选择?
| 场景 | 推荐方案 |
|---|---|
| 简单的手动处理(打印/日志) | 工具类 |
| 后端返回给前端的JSON自动脱敏 | 注解+Jackson序列化 |
| 数据库查询后全局脱敏(无侵入) | MyBatis插件 |
| 日志脱敏 | Logback/Layout转换器 |
| 非对称脱敏(保留可逆) | AES加密 + 脱敏规则组合(更安全) |
如果你的项目是 Spring Boot + Jackson(绝大多数),方案2(注解+Jackson序列化) 是最优雅、最常用的方式。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播