本文目录导读:

我来介绍几种常见的Java数据脱敏案例和实现方法。
基础字符串脱敏实现
public class DataMaskUtil {
/**
* 手机号脱敏:138****1234
*/
public static String maskPhone(String phone) {
if (phone == null || phone.length() != 11) {
return phone;
}
return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}
/**
* 身份证号脱敏:110101****1234
*/
public static String maskIdCard(String idCard) {
if (idCard == null || idCard.length() < 10) {
return idCard;
}
return idCard.replaceAll("(\\d{6})\\d{8,10}(\\w{4})", "$1********$2");
}
/**
* 邮箱脱敏:tes***@example.com
*/
public static String maskEmail(String email) {
if (email == null || !email.contains("@")) {
return email;
}
String[] parts = email.split("@");
String name = parts[0];
int length = name.length();
if (length <= 3) {
return name.substring(0, 1) + "***@" + parts[1];
}
return name.substring(0, 3) + "***@" + parts[1];
}
/**
* 姓名脱敏:张*或张**(保留姓)
*/
public static String maskName(String name) {
if (name == null || name.length() <= 1) {
return name;
}
String firstChar = name.substring(0, 1);
String mask = String.join("", Collections.nCopies(name.length() - 1, "*"));
return firstChar + mask;
}
/**
* 银行卡号脱敏:6222****1234
*/
public static String maskBankCard(String cardNo) {
if (cardNo == null || cardNo.length() < 8) {
return cardNo;
}
return cardNo.replaceAll("(\\d{4})\\d{8,12}(\\d{4})", "$1********$2");
}
/**
* 自定义脱敏
* @param str 原始字符串
* @param start 开始保留位数
* @param end 结尾保留位数
* @param maskChar 掩码字符
*/
public static String maskCustom(String str, int start, int end, char maskChar) {
if (str == null || str.isEmpty()) {
return str;
}
if (start + end >= str.length()) {
return str;
}
StringBuilder sb = new StringBuilder(str);
for (int i = start; i < str.length() - end; i++) {
sb.setCharAt(i, maskChar);
}
return sb.toString();
}
}
基于注解的AOP脱敏(推荐)
1 定义脱敏注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveField {
SensitiveType value() default SensitiveType.DEFAULT;
}
public enum SensitiveType {
PHONE, // 手机号
ID_CARD, // 身份证
EMAIL, // 邮箱
NAME, // 姓名
BANK_CARD, // 银行卡
ADDRESS, // 地址
DEFAULT // 默认脱敏
}
2 实体类使用注解
public class User {
private Long id;
@SensitiveField(SensitiveType.NAME)
private String name;
@SensitiveField(SensitiveType.PHONE)
private String phone;
@SensitiveField(SensitiveType.ID_CARD)
private String idCard;
@SensitiveField(SensitiveType.EMAIL)
private String email;
@SensitiveField(SensitiveType.BANK_CARD)
private String bankCard;
// getter/setter方法
}
3 AOP实现脱敏处理
@Aspect
@Component
public class SensitiveDataAspect {
@Around("@annotation(com.example.annotation.MaskData)")
public Object maskSensitiveData(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
if (result instanceof Collection) {
Collection<?> collection = (Collection<?>) result;
collection.forEach(this::maskObject);
} else if (result instanceof Page) {
Page<?> page = (Page<?>) result;
page.getContent().forEach(this::maskObject);
} else if (result != null) {
maskObject(result);
}
return result;
}
private void maskObject(Object obj) {
if (obj == null) return;
Class<?> clazz = obj.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
SensitiveField annotation = field.getAnnotation(SensitiveField.class);
if (annotation != null) {
field.setAccessible(true);
try {
Object value = field.get(obj);
if (value instanceof String && !((String) value).isEmpty()) {
String maskedValue = maskByType((String) value, annotation.value());
field.set(obj, maskedValue);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
private String maskByType(String value, SensitiveType type) {
switch (type) {
case PHONE:
return DataMaskUtil.maskPhone(value);
case ID_CARD:
return DataMaskUtil.maskIdCard(value);
case EMAIL:
return DataMaskUtil.maskEmail(value);
case NAME:
return DataMaskUtil.maskName(value);
case BANK_CARD:
return DataMaskUtil.maskBankCard(value);
default:
return value;
}
}
}
4 自定义脱敏注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MaskData {
String description() default "";
}
JSON序列化脱敏
1 自定义序列化器
public class SensitiveInfoSerializer extends JsonSerializer<String> {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
String maskedValue = maskSensitiveInfo(value);
gen.writeString(maskedValue);
}
private String maskSensitiveInfo(String value) {
// 根据实际需求实现脱敏逻辑
if (value.matches("^1[3-9]\\d{9}$")) {
return DataMaskUtil.maskPhone(value);
}
return value;
}
}
2 实体类使用序列化器
public class UserVO {
private Long id;
@JsonSerialize(using = SensitiveInfoSerializer.class)
private String name;
@JsonSerialize(using = SensitiveInfoSerializer.class)
private String phone;
// 其他字段
}
使用Jackson的脱敏工具类
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class JsonMaskUtil {
private static final ObjectMapper mapper = new ObjectMapper();
static {
SimpleModule module = new SimpleModule();
module.addSerializer(String.class, new JsonSerializer<String>() {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
String maskedValue = applyMask(value);
gen.writeString(maskedValue);
}
private String applyMask(String value) {
// 根据正则判断不同类型并脱敏
if (value.matches("^1[3-9]\\d{9}$")) {
return DataMaskUtil.maskPhone(value);
}
// 其他脱敏规则...
return value;
}
});
mapper.registerModule(module);
}
public static String toMaskedJson(Object obj) throws Exception {
return mapper.writeValueAsString(obj);
}
}
数据库查询时脱敏(MyBatis示例)
@Mapper
public interface UserMapper {
// 方式1:SQL层脱敏
@Select("SELECT id, " +
"CONCAT(LEFT(name, 1), '****', RIGHT(name, LENGTH(name) - LENGTH(LEFT(name, 1)))) as name, " +
"CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4)) as phone " +
"FROM user WHERE id = #{id}")
UserVO findByIdWithMask(@Param("id") Long id);
// 方式2:使用自定义TypeHandler
@Select("SELECT * FROM user WHERE id = #{id}")
@Results({
@Result(property = "phone", column = "phone", typeHandler = PhoneTypeHandler.class)
})
UserVO findByIdWithHandler(@Param("id") Long id);
}
// 自定义TypeHandler
public class PhoneTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
String value = rs.getString(columnName);
return DataMaskUtil.maskPhone(value);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String value = rs.getString(columnIndex);
return DataMaskUtil.maskPhone(value);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String value = cs.getString(columnIndex);
return DataMaskUtil.maskPhone(value);
}
}
完整的使用示例
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@MaskData // AOP自动脱敏
public List<UserVO> getUsers() {
return userMapper.findAll();
}
// 手动脱敏
public UserVO getUserWithMask(Long id) {
UserVO user = userMapper.findById(id);
user.setPhone(DataMaskUtil.maskPhone(user.getPhone()));
user.setName(DataMaskUtil.maskName(user.getName()));
return user;
}
// JSON序列化脱敏
public String getUserJson(Long id) throws Exception {
UserVO user = userMapper.findById(id);
return JsonMaskUtil.toMaskedJson(user);
}
}
推荐实践建议
- 使用AOP统一处理:在服务层或控制层统一进行脱敏,避免重复代码
- 区分脱敏场景:日志脱敏、前端展示脱敏、API响应脱敏可以用不同策略
- 数据权限控制:普通用户和管理员可以看到不同层级的脱敏数据
- 性能优化:使用缓存,避免每次查询都进行脱敏计算
方案可以根据实际项目需求灵活选择或组合使用。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播