当前位置: 首页 > news >正文

SpringBoot实现数据脱敏

文章目录

  • `SpringBoot`实现数据脱敏
    • 一、基于实体类属性的 `get`/`set` 方法
    • 二、基于 `MyBatis TypeHandler`
    • 三、基于实体类属性注解与 `Spring AOP`
    • 四、基于实体类属性注解与 `Jackson` 序列化机制

SpringBoot实现数据脱敏

一、基于实体类属性的 get/set 方法

优点:

  • 实现简单,易于理解
  • 无侵入业务逻辑
  • 统一控制,便于维护

缺点:

  • 需要大量重写get/set 方法
  • 只适用查询,不适用新增/修改

二、基于 MyBatis TypeHandler

注:TypeHandler 更适用于:加解密敏感字段,安全性高,且业务逻辑不受影响

优点:

  • 可同时实现加密存入,脱敏取出
  • 无侵入业务逻辑
  • 统一控制,便于维护

缺点:

  • 需要大量重写 resultMap 返回结果类型
  • 脱敏后业务层无法使用原始数据

示例:

自定义 TypeHandler 后,在 MyBatis 的映射文件中通过 typeHandler 属性引用即可生效。
注:IdCardUtils可自行实现,此处不做展示

public class IdCardTypeHandler extends BaseTypeHandler<String> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {ps.setString(i, IdCardUtils.publicEncrypt(parameter)); // 在设置参数时加密}@Overridepublic String getNullableResult(ResultSet rs, String columnName) throws SQLException {return IdCardUtils.privateDecrypt(rs.getString(columnName)); // 查询结果时解密,脱敏}@Overridepublic String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return IdCardUtils.privateDecrypt(rs.getString(columnIndex)); // 读取:按列名}@Overridepublic String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return IdCardUtils.privateDecrypt(cs.getString(columnIndex)); // 读取:存储过程}
}
<resultMap id="UserResult" type="User"><result property="idCard" column="id_card" typeHandler="IdCardTypeHandler"/>
</resultMap>

三、基于实体类属性注解与 Spring AOP

优点:

  • 可扩展性强
  • 无侵入业务逻辑
  • 统一控制,便于维护

缺点:

  • AOP 代理限制,避免方法在同类中调用
  • 不适用于基本类型数组和 Map
  • 嵌套对象需递归处理

示例:

1、枚举脱敏类型、定义注解、工具类,参考第4种方式中实现,此处不做重复展示
2、实现切面类

注:不是springboot版本需手动配置AOP,扫描范围根据实际情况自行配置

<!-- 1. 扫描组件:Service、Repository、Aspect 等 -->
<context:component-scan base-package="com.module" /><!-- 2. 启用 AspectJ 自动代理(AOP 生效的关键) -->
<aop:aspectj-autoproxy proxy-target-class="true" />
@Aspect
@Component
public class SensitiveDataAspect //切面类实现脱敏逻辑
{		/*** 拦截所有 Service 方法的返回值*/@AfterReturning(pointcut = "execution(* com.service..*.*(..))", returning = "result")public void desensitize(Object result){if (result == null) return;if (result instanceof Collection){Collection<?> collection = (Collection<?>)result;for (Object obj : collection){desensitizeObject(obj);}}else if (result.getClass().isArray()){Object[] array = (Object[])result;for (Object obj : array){desensitizeObject(obj);}}else{desensitizeObject(result);}}private void desensitizeObject(Object obj){if (obj == null) return;Class<?> clazz = obj.getClass();Field[] fields = clazz.getDeclaredFields();for (Field field : fields){try{Sensitive sensitive = field.getAnnotation(Sensitive.class);if (sensitive != null){field.setAccessible(true);Object value = field.get(obj);if (value instanceof String){String masked = sensitive.value().desensitizer().apply((String) value);field.set(obj, masked);} else {// 递归处理嵌套对象desensitizeObject(value);}}}catch (Exception e){//处理日志。。。}}}
}

3、在实体类属性中加上注解

@Sensitive(SensitiveType.PHONE)
private String userName;//该字段需要脱敏,加上注解

四、基于实体类属性注解与 Jackson 序列化机制

优点:

  • 脱敏时机精准
  • 性能更优
  • 天然支持嵌套结构

缺点:

  • 仅对 JSON 有效
  • 不适用于非 Jackson 场景

示例:
1、枚举脱敏类型

public enum DesensitizedType
{/*** 姓名,第2位星号替换*/USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),/*** 密码,全部字符都用*代替*/PASSWORD(DesensitizedUtil::password),/*** 身份证,中间10位星号替换*/ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{3}[Xx]|\\d{4})", "$1** **** ****$2")),/*** 手机号,中间4位星号替换*/PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),/*** 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换*/EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")),/*** 银行卡号,保留最后4位,其他星号替换*/BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")),/*** 车牌号码,包含普通车辆、新能源车辆*/CAR_LICENSE(DesensitizedUtil::carLicense);private final Function<String, String> desensitizer;DesensitizedType(Function<String, String> desensitizer){this.desensitizer = desensitizer;}public Function<String, String> desensitizer(){return desensitizer;}
}

2、脱敏工具类

public class DesensitizedUtil
{/*** 密码的全部字符都用*代替,比如:******** @param password 密码* @return 脱敏后的密码*/public static String password(String password){if (StringUtils.isBlank(password)){return StringUtils.EMPTY;}return StringUtils.repeat('*', password.length());}/*** 车牌中间用*代替,如果是错误的车牌,不处理** @param carLicense 完整的车牌号* @return 脱敏后的车牌*/public static String carLicense(String carLicense){if (StringUtils.isBlank(carLicense)){return StringUtils.EMPTY;}// 普通车牌if (carLicense.length() == 7){carLicense = StringUtils.hide(carLicense, 3, 6);}else if (carLicense.length() == 8){// 新能源车牌carLicense = StringUtils.hide(carLicense, 3, 7);}return carLicense;}/*** 根据输入的电话号码字符串,返回格式化后的电话号码字符串* 格式化后的电话号码将隐藏中间四位数字,以保护用户隐私* 如果输入的电话号码为空或不符合中国大陆电话号码的常见格式(11位数字),则返回空字符串** @param phoneNumber 用户输入的电话号码字符串* @return 格式化后的电话号码字符串,如果输入不合法,则返回空字符串*/public static String phoneNumber(String phoneNumber){if (StringUtils.isBlank(phoneNumber) || !Pattern.matches("^\\d{11}$", phoneNumber)) {return StringUtils.EMPTY;}return phoneNumber.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");}
}

3、定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive
{DesensitizedType desensitizedType();
}

4、自定义序列化

public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer
{private DesensitizedType desensitizedType;@Overridepublic void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException{gen.writeString(desensitizedType.desensitizer().apply(value));}@Overridepublic JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)throws JsonMappingException{Sensitive annotation = property.getAnnotation(Sensitive.class);if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())){this.desensitizedType = annotation.desensitizedType();return this;}return prov.findValueSerializer(property.getType(), property);}
}

5、在实体类属性中加上注解

@Sensitive(SensitiveType.PHONE)
private String userName;//该字段需要脱敏,加上注解
http://www.dtcms.com/a/465876.html

相关文章:

  • 基于JavaWeb的智慧养老院管理系统的设计与实现(代码+数据库+LW)
  • 网站建设项目执行情况报告模板北京海淀区
  • Qt:多文档模式开发
  • k8s集群环境下微服务项目性能实战(单接口)
  • 5分钟了解k8s pod通信原理--图文篇
  • 静态网页素材泉州seo优化排名公司
  • 建设银行网站上改手机东莞市常平东部中心医院
  • MySQL索引优化实战从慢查询到高性能的蜕变之路
  • Java中的Hook机制
  • MATLAB实现FCM和KFCM聚类算法
  • 讲述做网站的电影网站圣诞问候特效
  • 想在拼购网站做产品罗湖网站开发
  • 贝叶斯结合LSTM用于市场预测,准确性达新高度!
  • 老题新解|大整数减法
  • 品牌网官网查询外贸网站建设平台优化营销推广
  • 上海微信网站建设山东做网站建设的好公司排名
  • 记录一次巧妙的SQL:一对多关联导致的 sum () 、count()等group函数重复计算问题
  • 3.3 Function Calling实战
  • 无锡企业网站制作策划深圳海洋网络做网站
  • Maven 自动化部署
  • 阿里云做网站教程辽宁做网站找谁
  • Flutter中新手需要掌握的几种Widget
  • 分类算法-逻辑回归
  • MySQL Redo Log 和 Undo Log 满了会有什么问题
  • 从崩溃到稳定:如何用<limits>头文件解决C++数值处理的核心痛点?
  • 自定义tabs+索引列表,支持左右滑动切换
  • 建设网站的必要与可行性制作企业网站需要注意的事项
  • MySQL查询优化实战从慢查询到高性能的索引重构策略
  • 官方网站建设报价wordpress 在线咨询
  • 从零实现JSON与图片文件上传功能