前言
在开发系统过程中,经常会接触到大量的数据信息,这些数据信息可能包含身份证号、手机号、姓名、卡号等各种敏感信息。而有些用户需求是不允许,这些敏感信息数据为了保护用户个人信息的安全。对这些数据需要进行特殊处理。
数据脱敏又称数据去隐私化或数据变形,是在给定的规则、策略下对敏感数据进行变换、修改的技术机制,能够在很大程度上解决敏感数据在非可信环境中使用的问题。根据数据保护规范和脱敏策略.对业务数据中的敏感信息实施自动变形.实现对敏感信息的隐藏。
-
- /**
- * 需要脱敏的字段类型
- *
- * @author xh
- * @Date 2022/7/20
- */
- public enum SensitiveTypeEnum {
- /**
- * 中文名
- */
- CHINESE_NAME,
- /**
- * 身份证号
- */
- ID_CARD,
- /**
- * 座机号
- */
- FIXED_PHONE,
- /**
- * 手机号
- */
- MOBILE_PHONE,
- /**
- * 地址
- */
- ADDRESS,
- /**
- * 电子邮件
- */
- EMAIL,
- /**
- * 银行卡
- */
- BANK_CARD,
- /**
- * 虚拟账号
- */
- ACCOUNT,
- /**
- * 密码
- */
- PASSWORD;
- }
-
- import com.artboy.project.common.enums.SensitiveTypeEnum;
-
- import java.lang.annotation.*;
-
- /**
- * @author xh
- * @Date 2022/7/20
- */
- // 指定在注解使用的位置
- @Target({ElementType.FIELD, ElementType.METHOD})
- // 指定注解保存的范围
- @Retention(RetentionPolicy.RUNTIME)
- // 可以被子类继承
- @Inherited
- // 设置为文档说明
- @Documented
- public @interface Desensitized {
- /*脱敏类型(规则)*/
- SensitiveTypeEnum type();
-
- /*判断注解是否生效的方法*/
- String isEffictiveMethod() default "";
- }
用于复制对象和对对象的其他操作,注意使用fastjson实现深拷贝对于复杂的对象会出现栈溢出,这里修改为了Gson
- package com.artboy.project.common.utils;
-
- import com.google.gson.Gson;
- import org.apache.commons.lang3.StringUtils;
-
- import java.io.*;
- import java.lang.reflect.Array;
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Modifier;
- import java.util.*;
-
- /**
- * @author xh
- * @Date 2022/7/26
- */
- public class DesensitizedObjectUtils {
- /**
- * 用序列化-反序列化方式实现深克隆
- * 缺点:1、被拷贝的对象必须要实现序列化
- *
- * @param obj
- * @return
- */
- @SuppressWarnings("unchecked")
- public static <T> T deepCloneObject(T obj) {
- T t = (T) new Object();
- try {
- ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
- ObjectOutputStream out = new ObjectOutputStream(byteOut);
- out.writeObject(obj);
- out.close();
- ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
- ObjectInputStream in = new ObjectInputStream(byteIn);
- t = (T) in.readObject();
- in.close();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- return t;
- }
-
- /**
- * 用序列化-反序列化的方式实现深克隆
- * 这里使用GSON来取代FAST-JSON
- * 缺点:1、当实体中存在接口类型的参数,并且这个接口指向的实例为枚举类型时,会报错"com.alibaba.fastjson.JSONException: syntax error, expect {, actual string, pos 171, fieldName iLimitKey"
- *
- * @param objSource
- * @return
- */
- public static Object deepCloneByFastJson(Object objSource) {
- // String tempJson = JSON.toJSONString(objSource);
- // Object clone = JSON.parseObject(tempJson, objSource.getClass());
- Gson gson = new Gson();
- String tempJson = gson.toJson(objSource);
- Object clone = gson.fromJson(tempJson, objSource.getClass());
- return clone;
- }
-
- /**
- * 深度克隆对象
- *
- * @throws IllegalAccessException
- * @throws InstantiationException
- */
- public static Object deepClone(Object objSource) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
- if (null == objSource) return null;
- //是否jdk类型、基础类型、枚举类型
- if (isJDKType(objSource.getClass())
- || objSource.getClass().isPrimitive()
- || objSource instanceof Enum<?>) {
- if ("java.lang.String".equals(objSource.getClass().getName())) {//目前只支持String类型深复制
- return new String((String) objSource);
- } else {
- return objSource;
- }
- }
- // 获取源对象类型
- Class<?> clazz = objSource.getClass();
- Object objDes = clazz.newInstance();
- // 获得源对象所有属性
- Field[] fields = getAllFields(objSource);
- // 循环遍历字段,获取字段对应的属性值
- for (Field field : fields) {
- field.setAccessible(true);
- if (null == field) continue;
- Object value = field.get(objSource);
- if (null == value) continue;
- Class<?> type = value.getClass();
- if (isStaticFinal(field)) {
- continue;
- }
- try {
-
- //遍历集合属性
- if (type.isArray()) {//对数组类型的字段进行递归过滤
- int len = Array.getLength(value);
- if (len < 1) continue;
- Class<?> c = value.getClass().getComponentType();
- Array newArray = (Array) Array.newInstance(c, len);
- for (int i = 0; i < len; i++) {
- Object arrayObject = Array.get(value, i);
- Array.set(newArray, i, deepClone(arrayObject));
- }
- } else if (value instanceof Collection<?>) {
- Collection newCollection = (Collection) value.getClass().newInstance();
- Collection<?> c = (Collection<?>) value;
- Iterator<?> it = c.iterator();
- while (it.hasNext()) {
- Object collectionObj = it.next();
- newCollection.add(deepClone(collectionObj));
- }
- field.set(objDes, newCollection);
- continue;
- } else if (value instanceof Map<?, ?>) {
- Map newMap = (Map) value.getClass().newInstance();
- Map<?, ?> m = (Map<?, ?>) value;
- Set<?> set = m.entrySet();
- for (Object o : set) {
- Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
- Object mapVal = entry.getValue();
- newMap.put(entry.getKey(), deepClone(mapVal));
- }
- field.set(objDes, newMap);
- continue;
- }
-
- //是否jdk类型或基础类型
- if (isJDKType(field.get(objSource).getClass())
- || field.getClass().isPrimitive()
- || isStaticType(field)
- || value instanceof Enum<?>) {
- if ("java.lang.String".equals(value.getClass().getName())) {//目前只支持String类型深复制
- field.set(objDes, new String((String) value));
- } else {
- field.set(objDes, field.get(objSource));
- }
- continue;
- }
-
- //是否枚举
- if (value.getClass().isEnum()) {
- field.set(objDes, field.get(objSource));
- continue;
- }
-
- //是否自定义类
- if (isUserDefinedType(value.getClass())) {
- field.set(objDes, deepClone(value));
- continue;
- }
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return objDes;
- }
-
-
- /**
- * 是否静态变量
- *
- * @param field
- * @return
- */
- private static boolean isStaticType(Field field) {
- return field.getModifiers() == 8 ? true : false;
- }
-
- private static boolean isStaticFinal(Field field) {
- return Modifier.isFinal(field.getModifiers()) && Modifier.isStatic(field.getModifiers());
- }
-
- /**
- * 是否jdk类型变量
- *
- * @param clazz
- * @return
- * @throws IllegalAccessException
- */
- private static boolean isJDKType(Class clazz) throws IllegalAccessException {
- //Class clazz = field.get(objSource).getClass();
- return org.apache.commons.lang3.StringUtils.startsWith(clazz.getPackage().getName(), "javax.")
- || org.apache.commons.lang3.StringUtils.startsWith(clazz.getPackage().getName(), "java.")
- || org.apache.commons.lang3.StringUtils.startsWith(clazz.getName(), "javax.")
- || org.apache.commons.lang3.StringUtils.startsWith(clazz.getName(), "java.");
- }
-
- /**
- * 是否用户自定义类型
- *
- * @param clazz
- * @return
- */
- private static boolean isUserDefinedType(Class<?> clazz) {
- return
- clazz.getPackage() != null
- && !org.apache.commons.lang3.StringUtils.startsWith(clazz.getPackage().getName(), "javax.")
- && !org.apache.commons.lang3.StringUtils.startsWith(clazz.getPackage().getName(), "java.")
- && !org.apache.commons.lang3.StringUtils.startsWith(clazz.getName(), "javax.")
- && !StringUtils.startsWith(clazz.getName(), "java.");
- }
-
- /**
- * 获取包括父类所有的属性
- *
- * @param objSource
- * @return
- */
- public static Field[] getAllFields(Object objSource) {
- /*获得当前类的所有属性(private、protected、public)*/
- List<Field> fieldList = new ArrayList<Field>();
- Class tempClass = objSource.getClass();
- while (tempClass != null && !tempClass.getName().toLowerCase().equals("java.lang.object")) {//当父类为null的时候说明到达了最上层的父类(Object类).
- fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields()));
- tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
- }
- Field[] fields = new Field[fieldList.size()];
- fieldList.toArray(fields);
- return fields;
- }
-
- /**
- * 深度克隆对象
- *
- * @throws IllegalAccessException
- * @throws InstantiationException
- */
- @Deprecated
- public static Object copy(Object objSource) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-
- if (null == objSource) return null;
- // 获取源对象类型
- Class<?> clazz = objSource.getClass();
- Object objDes = clazz.newInstance();
- // 获得源对象所有属性
- Field[] fields = getAllFields(objSource);
- // 循环遍历字段,获取字段对应的属性值
- for (Field field : fields) {
- field.setAccessible(true);
- // 如果该字段是 static + final 修饰
- if (field.getModifiers() >= 24) {
- continue;
- }
- try {
- // 设置字段可见,即可用get方法获取属性值。
- field.set(objDes, field.get(objSource));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return objDes;
- }
- }
-
- package com.artboy.project.common.utils;
-
- import com.artboy.project.common.annotation.Desensitized;
- import com.google.gson.Gson;
- import org.apache.commons.lang3.StringUtils;
-
- import java.lang.reflect.Array;
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.*;
-
- /**
- * @author xh
- * @Date 2022/7/26
- */
- public class DesensitizedUtils {
- /**
- * 获取脱敏json串(递归引用会导致java.lang.StackOverflowError)
- *
- * @param javaBean
- * @return
- */
- public static String getJson(Object javaBean) {
- String json = null;
- if (null != javaBean) {
- try {
- if (javaBean.getClass().isInterface()) return json;
- /* 克隆出一个实体进行字段修改,避免修改原实体 */
- //Object clone =DesensitizedObjectUtils.deepCloneObject(javaBean);
- //Object clone =DesensitizedObjectUtils.deepCloneByFastJson(javaBean);
- Object clone = DesensitizedObjectUtils.deepClone(javaBean);
-
- /* 定义一个计数器,用于避免重复循环自定义对象类型的字段 */
- Set<Integer> referenceCounter = new HashSet<Integer>();
-
- /* 对克隆实体进行脱敏操作 */
- DesensitizedUtils.replace(DesensitizedObjectUtils.getAllFields(clone), clone, referenceCounter);
-
- /* 利用fastjson对脱敏后的克隆对象进行序列化 */
- // json = JSON.toJSONString(clone, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty);
- json = new Gson().toJson(clone);
-
- /* 清空计数器 */
- referenceCounter.clear();
- referenceCounter = null;
- } catch (Throwable e) {
- e.printStackTrace();
- }
- }
- return json;
- }
-
- public static <T> T getObj(T javaBean) {
- T clone = null;
- if (null != javaBean) {
- try {
- if (javaBean.getClass().isInterface()) return null;
- /* 克隆出一个实体进行字段修改,避免修改原实体 */
- //Object clone =DesensitizedObjectUtils.deepCloneObject(javaBean);
- //Object clone =DesensitizedObjectUtils.deepCloneByFastJson(javaBean);
- clone = (T) DesensitizedObjectUtils.deepClone(javaBean);
-
- /* 定义一个计数器,用于避免重复循环自定义对象类型的字段 */
- Set<Integer> referenceCounter = new HashSet<Integer>();
-
- /* 对克隆实体进行脱敏操作 */
- DesensitizedUtils.replace(DesensitizedObjectUtils.getAllFields(clone), clone, referenceCounter);
-
- /* 清空计数器 */
- referenceCounter.clear();
- referenceCounter = null;
- } catch (Throwable e) {
- e.printStackTrace();
- }
- }
- return clone;
- }
-
- /**
- * 对需要脱敏的字段进行转化
- *
- * @param fields
- * @param javaBean
- * @param referenceCounter
- * @throws IllegalArgumentException
- * @throws IllegalAccessException
- */
- private static void replace(Field[] fields, Object javaBean, Set<Integer> referenceCounter) throws IllegalArgumentException, IllegalAccessException {
- if (null != fields && fields.length > 0) {
- for (Field field : fields) {
- field.setAccessible(true);
- if (null != field && null != javaBean) {
- Object value = field.get(javaBean);
- if (null != value) {
- Class<?> type = value.getClass();
- //处理子属性,包括集合中的
- if (type.isArray()) {//对数组类型的字段进行递归过滤
- int len = Array.getLength(value);
- for (int i = 0; i < len; i++) {
- Object arrayObject = Array.get(value, i);
- if (isNotGeneralType(arrayObject.getClass(), arrayObject, referenceCounter)) {
- replace(DesensitizedObjectUtils.getAllFields(arrayObject), arrayObject, referenceCounter);
- }
- }
- } else if (value instanceof Collection<?>) {//对集合类型的字段进行递归过滤
- Collection<?> c = (Collection<?>) value;
- Iterator<?> it = c.iterator();
- while (it.hasNext()) {// TODO: 待优化
- Object collectionObj = it.next();
- if (isNotGeneralType(collectionObj.getClass(), collectionObj, referenceCounter)) {
- replace(DesensitizedObjectUtils.getAllFields(collectionObj), collectionObj, referenceCounter);
- }
- }
- } else if (value instanceof Map<?, ?>) {//对Map类型的字段进行递归过滤
- Map<?, ?> m = (Map<?, ?>) value;
- Set<?> set = m.entrySet();
- for (Object o : set) {
- Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
- Object mapVal = entry.getValue();
- if (isNotGeneralType(mapVal.getClass(), mapVal, referenceCounter)) {
- replace(DesensitizedObjectUtils.getAllFields(mapVal), mapVal, referenceCounter);
- }
- }
- } else if (value instanceof Enum<?>) {
- continue;
- }
-
- /*除基础类型、jdk类型的字段之外,对其他类型的字段进行递归过滤*/
- else {
- if (!type.isPrimitive()
- && type.getPackage() != null
- && !StringUtils.startsWith(type.getPackage().getName(), "javax.")
- && !StringUtils.startsWith(type.getPackage().getName(), "java.")
- && !StringUtils.startsWith(field.getType().getName(), "javax.")
- && !StringUtils.startsWith(field.getName(), "java.")
- && referenceCounter.add(value.hashCode())) {
- replace(DesensitizedObjectUtils.getAllFields(value), value, referenceCounter);
- }
- }
- }
-
- //脱敏操作
- setNewValueForField(javaBean, field, value);
-
- }
- }
- }
- }
-
- /**
- * 排除基础类型、jdk类型、枚举类型的字段
- *
- * @param clazz
- * @param value
- * @param referenceCounter
- * @return
- */
- private static boolean isNotGeneralType(Class<?> clazz, Object value, Set<Integer> referenceCounter) {
- return !clazz.isPrimitive()
- && clazz.getPackage() != null
- && !clazz.isEnum()
- && !StringUtils.startsWith(clazz.getPackage().getName(), "javax.")
- && !StringUtils.startsWith(clazz.getPackage().getName(), "java.")
- && !StringUtils.startsWith(clazz.getName(), "javax.")
- && !StringUtils.startsWith(clazz.getName(), "java.")
- && referenceCounter.add(value.hashCode());
- }
-
- /**
- * 脱敏操作(按照规则转化需要脱敏的字段并设置新值)
- * 目前只支持String类型的字段,如需要其他类型如BigDecimal、Date等类型,可以添加
- *
- * @param javaBean
- * @param field
- * @param value
- * @throws IllegalAccessException
- */
- public static void setNewValueForField(Object javaBean, Field field, Object value) throws IllegalAccessException {
- //处理自身的属性
- Desensitized annotation = field.getAnnotation(Desensitized.class);
- if (field.getType().equals(String.class) && null != annotation && executeIsEffictiveMethod(javaBean, annotation)) {
- String valueStr = (String) value;
- if (StringUtils.isNotBlank(valueStr)) {
- switch (annotation.type()) {
- case CHINESE_NAME: {
- field.set(javaBean, DesensitizedUtils.chineseName(valueStr));
- break;
- }
- case ID_CARD: {
- field.set(javaBean, DesensitizedUtils.idCardNum(valueStr));
- break;
- }
- case FIXED_PHONE: {
- field.set(javaBean, DesensitizedUtils.fixedPhone(valueStr));
- break;
- }
- case MOBILE_PHONE: {
- field.set(javaBean, DesensitizedUtils.mobilePhone(valueStr));
- break;
- }
- case ADDRESS: {
- field.set(javaBean, DesensitizedUtils.address(valueStr, 7));
- break;
- }
- case EMAIL: {
- field.set(javaBean, DesensitizedUtils.email(valueStr));
- break;
- }
- case BANK_CARD: {
- field.set(javaBean, DesensitizedUtils.bankCard(valueStr));
- break;
- }
- case PASSWORD: {
- field.set(javaBean, DesensitizedUtils.password(valueStr));
- break;
- }case ACCOUNT:{
- field.set(javaBean, DesensitizedUtils.account(valueStr));
- break;
- }
- }
- }
- }
- }
-
- /**
- * 执行某个对象中指定的方法
- *
- * @param javaBean 对象
- * @param desensitized
- * @return
- */
- private static boolean executeIsEffictiveMethod(Object javaBean, Desensitized desensitized) {
- boolean isAnnotationEffictive = true;//注解默认生效
- if (desensitized != null) {
- String isEffictiveMethod = desensitized.isEffictiveMethod();
- if (isNotEmpty(isEffictiveMethod)) {
- try {
- Method method = javaBean.getClass().getMethod(isEffictiveMethod);
- method.setAccessible(true);
- isAnnotationEffictive = (Boolean) method.invoke(javaBean);
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- }
- }
- return isAnnotationEffictive;
- }
-
- private static boolean isNotEmpty(String str) {
- return str != null && !"".equals(str);
- }
-
- private static boolean isEmpty(String str) {
- return !isNotEmpty(str);
- }
-
- /**
- * 【中文姓名】只显示第一个汉字,其他隐藏为2个星号,比如:李**
- *
- * @param fullName
- * @return
- */
- public static String chineseName(String fullName) {
- if (StringUtils.isBlank(fullName)) {
- return "";
- }
- String name = StringUtils.left(fullName, 1);
- return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
- }
-
- /**
- * 【身份证号】显示第一位和最后一位
- *
- * @param id
- * @return
- */
- public static String idCardNum(String id) {
- if (StringUtils.isBlank(id)) {
- return "";
- }
- return StringUtils.left(id,1).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(id,1), StringUtils.length(id),"*"),"*"));
- }
-
- /**
- * 【虚拟账号】显示第一位和最后一位
- *
- * @param id
- * @return
- */
- public static String account(String id) {
- if (StringUtils.isBlank(id)) {
- return "";
- }
- return StringUtils.left(id,1).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(id,1), StringUtils.length(id),"*"),"*"));
- }
-
-
- /**
- * 【固定电话 后四位,其他隐藏,比如1234
- *
- * @param num
- * @return
- */
- public static String fixedPhone(String num) {
- if (StringUtils.isBlank(num)) {
- return "";
- }
- return StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*");
- }
-
- /**
- * 【手机号码】前三位,后四位,其他隐藏,比如135****6810
- *
- * @param num
- * @return
- */
- public static String mobilePhone(String num) {
- if (StringUtils.isBlank(num)) {
- return "";
- }
- return StringUtils.left(num, 3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*"), "***"));
- }
-
- /**
- * 【地址】只显示到地区,不显示详细地址,比如:北京市海淀区****
- *
- * @param address
- * @param sensitiveSize 敏感信息长度
- * @return
- */
- public static String address(String address, int sensitiveSize) {
- if (StringUtils.isBlank(address)) {
- return "";
- }
- int length = StringUtils.length(address);
- return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length, "*");
- }
-
- /**
- * 【电子邮箱 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com>
- *
- * @param email
- * @return
- */
- public static String email(String email) {
- if (StringUtils.isBlank(email)) {
- return "";
- }
- int index = StringUtils.indexOf(email, "@");
- if (index <= 1)
- return email;
- else
- return StringUtils.rightPad(StringUtils.left(email, 1), index, "*").concat(StringUtils.mid(email, index, StringUtils.length(email)));
- }
-
- /**
- * 【银行卡号】前4位,后3位,其他用星号隐藏每位1个星号,比如:6217 **** **** **** 567>
- *
- * @param cardNum
- * @return
- */
- public static String bankCard(String cardNum) {
- if (StringUtils.isBlank(cardNum)) {
- return "";
- }
- return StringUtils.left(cardNum, 4).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum, 3), StringUtils.length(cardNum), "*"), "****"));
- }
-
- /**
- * 【密码】密码的全部字符都用*代替,比如:******
- *
- * @param password
- * @return
- */
- public static String password(String password) {
- if (StringUtils.isBlank(password)) {
- return "";
- }
- String pwd = StringUtils.left(password, 0);
- return StringUtils.rightPad(pwd, StringUtils.length(password), "*");
- }
-
- /**
- * 遍历List脱敏数据
- * @param content
- * @return
- */
- public static <T> List getList(List<T> content){
- if (content == null || content.size() <= 0) {
- return content;
- }
- List list = new ArrayList<T>();
- for (T t : content) {
- list.add(getObj(t));
- }
- return list;
- }
- }
-
- package com.artboy.project.common.utils;
-
- import com.artboy.project.common.annotation.Desensitized;
- import com.google.gson.Gson;
- import org.apache.commons.lang3.StringUtils;
-
- import java.lang.reflect.Array;
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.*;
-
- /**
- * @author xh
- * @Date 2022/7/26
- */
- public class DesensitizedUtils {
- /**
- * 获取脱敏json串(递归引用会导致java.lang.StackOverflowError)
- *
- * @param javaBean
- * @return
- */
- public static String getJson(Object javaBean) {
- String json = null;
- if (null != javaBean) {
- try {
- if (javaBean.getClass().isInterface()) return json;
- /* 克隆出一个实体进行字段修改,避免修改原实体 */
- //Object clone =DesensitizedObjectUtils.deepCloneObject(javaBean);
- //Object clone =DesensitizedObjectUtils.deepCloneByFastJson(javaBean);
- Object clone = DesensitizedObjectUtils.deepClone(javaBean);
-
- /* 定义一个计数器,用于避免重复循环自定义对象类型的字段 */
- Set<Integer> referenceCounter = new HashSet<Integer>();
-
- /* 对克隆实体进行脱敏操作 */
- DesensitizedUtils.replace(DesensitizedObjectUtils.getAllFields(clone), clone, referenceCounter);
-
- /* 利用fastjson对脱敏后的克隆对象进行序列化 */
- // json = JSON.toJSONString(clone, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty);
- json = new Gson().toJson(clone);
-
- /* 清空计数器 */
- referenceCounter.clear();
- referenceCounter = null;
- } catch (Throwable e) {
- e.printStackTrace();
- }
- }
- return json;
- }
-
- public static <T> T getObj(T javaBean) {
- T clone = null;
- if (null != javaBean) {
- try {
- if (javaBean.getClass().isInterface()) return null;
- /* 克隆出一个实体进行字段修改,避免修改原实体 */
- //Object clone =DesensitizedObjectUtils.deepCloneObject(javaBean);
- //Object clone =DesensitizedObjectUtils.deepCloneByFastJson(javaBean);
- clone = (T) DesensitizedObjectUtils.deepClone(javaBean);
-
- /* 定义一个计数器,用于避免重复循环自定义对象类型的字段 */
- Set<Integer> referenceCounter = new HashSet<Integer>();
-
- /* 对克隆实体进行脱敏操作 */
- DesensitizedUtils.replace(DesensitizedObjectUtils.getAllFields(clone), clone, referenceCounter);
-
- /* 清空计数器 */
- referenceCounter.clear();
- referenceCounter = null;
- } catch (Throwable e) {
- e.printStackTrace();
- }
- }
- return clone;
- }
-
- /**
- * 对需要脱敏的字段进行转化
- *
- * @param fields
- * @param javaBean
- * @param referenceCounter
- * @throws IllegalArgumentException
- * @throws IllegalAccessException
- */
- private static void replace(Field[] fields, Object javaBean, Set<Integer> referenceCounter) throws IllegalArgumentException, IllegalAccessException {
- if (null != fields && fields.length > 0) {
- for (Field field : fields) {
- field.setAccessible(true);
- if (null != field && null != javaBean) {
- Object value = field.get(javaBean);
- if (null != value) {
- Class<?> type = value.getClass();
- //处理子属性,包括集合中的
- if (type.isArray()) {//对数组类型的字段进行递归过滤
- int len = Array.getLength(value);
- for (int i = 0; i < len; i++) {
- Object arrayObject = Array.get(value, i);
- if (isNotGeneralType(arrayObject.getClass(), arrayObject, referenceCounter)) {
- replace(DesensitizedObjectUtils.getAllFields(arrayObject), arrayObject, referenceCounter);
- }
- }
- } else if (value instanceof Collection<?>) {//对集合类型的字段进行递归过滤
- Collection<?> c = (Collection<?>) value;
- Iterator<?> it = c.iterator();
- while (it.hasNext()) {// TODO: 待优化
- Object collectionObj = it.next();
- if (isNotGeneralType(collectionObj.getClass(), collectionObj, referenceCounter)) {
- replace(DesensitizedObjectUtils.getAllFields(collectionObj), collectionObj, referenceCounter);
- }
- }
- } else if (value instanceof Map<?, ?>) {//对Map类型的字段进行递归过滤
- Map<?, ?> m = (Map<?, ?>) value;
- Set<?> set = m.entrySet();
- for (Object o : set) {
- Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
- Object mapVal = entry.getValue();
- if (isNotGeneralType(mapVal.getClass(), mapVal, referenceCounter)) {
- replace(DesensitizedObjectUtils.getAllFields(mapVal), mapVal, referenceCounter);
- }
- }
- } else if (value instanceof Enum<?>) {
- continue;
- }
-
- /*除基础类型、jdk类型的字段之外,对其他类型的字段进行递归过滤*/
- else {
- if (!type.isPrimitive()
- && type.getPackage() != null
- && !StringUtils.startsWith(type.getPackage().getName(), "javax.")
- && !StringUtils.startsWith(type.getPackage().getName(), "java.")
- && !StringUtils.startsWith(field.getType().getName(), "javax.")
- && !StringUtils.startsWith(field.getName(), "java.")
- && referenceCounter.add(value.hashCode())) {
- replace(DesensitizedObjectUtils.getAllFields(value), value, referenceCounter);
- }
- }
- }
-
- //脱敏操作
- setNewValueForField(javaBean, field, value);
-
- }
- }
- }
- }
-
- /**
- * 排除基础类型、jdk类型、枚举类型的字段
- *
- * @param clazz
- * @param value
- * @param referenceCounter
- * @return
- */
- private static boolean isNotGeneralType(Class<?> clazz, Object value, Set<Integer> referenceCounter) {
- return !clazz.isPrimitive()
- && clazz.getPackage() != null
- && !clazz.isEnum()
- && !StringUtils.startsWith(clazz.getPackage().getName(), "javax.")
- && !StringUtils.startsWith(clazz.getPackage().getName(), "java.")
- && !StringUtils.startsWith(clazz.getName(), "javax.")
- && !StringUtils.startsWith(clazz.getName(), "java.")
- && referenceCounter.add(value.hashCode());
- }
-
- /**
- * 脱敏操作(按照规则转化需要脱敏的字段并设置新值)
- * 目前只支持String类型的字段,如需要其他类型如BigDecimal、Date等类型,可以添加
- *
- * @param javaBean
- * @param field
- * @param value
- * @throws IllegalAccessException
- */
- public static void setNewValueForField(Object javaBean, Field field, Object value) throws IllegalAccessException {
- //处理自身的属性
- Desensitized annotation = field.getAnnotation(Desensitized.class);
- if (field.getType().equals(String.class) && null != annotation && executeIsEffictiveMethod(javaBean, annotation)) {
- String valueStr = (String) value;
- if (StringUtils.isNotBlank(valueStr)) {
- switch (annotation.type()) {
- case CHINESE_NAME: {
- field.set(javaBean, DesensitizedUtils.chineseName(valueStr));
- break;
- }
- case ID_CARD: {
- field.set(javaBean, DesensitizedUtils.idCardNum(valueStr));
- break;
- }
- case FIXED_PHONE: {
- field.set(javaBean, DesensitizedUtils.fixedPhone(valueStr));
- break;
- }
- case MOBILE_PHONE: {
- field.set(javaBean, DesensitizedUtils.mobilePhone(valueStr));
- break;
- }
- case ADDRESS: {
- field.set(javaBean, DesensitizedUtils.address(valueStr, 7));
- break;
- }
- case EMAIL: {
- field.set(javaBean, DesensitizedUtils.email(valueStr));
- break;
- }
- case BANK_CARD: {
- field.set(javaBean, DesensitizedUtils.bankCard(valueStr));
- break;
- }
- case PASSWORD: {
- field.set(javaBean, DesensitizedUtils.password(valueStr));
- break;
- }case ACCOUNT:{
- field.set(javaBean, DesensitizedUtils.account(valueStr));
- break;
- }
- }
- }
- }
- }
-
- /**
- * 执行某个对象中指定的方法
- *
- * @param javaBean 对象
- * @param desensitized
- * @return
- */
- private static boolean executeIsEffictiveMethod(Object javaBean, Desensitized desensitized) {
- boolean isAnnotationEffictive = true;//注解默认生效
- if (desensitized != null) {
- String isEffictiveMethod = desensitized.isEffictiveMethod();
- if (isNotEmpty(isEffictiveMethod)) {
- try {
- Method method = javaBean.getClass().getMethod(isEffictiveMethod);
- method.setAccessible(true);
- isAnnotationEffictive = (Boolean) method.invoke(javaBean);
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- }
- }
- return isAnnotationEffictive;
- }
-
- private static boolean isNotEmpty(String str) {
- return str != null && !"".equals(str);
- }
-
- private static boolean isEmpty(String str) {
- return !isNotEmpty(str);
- }
-
- /**
- * 【中文姓名】只显示第一个汉字,其他隐藏为2个星号,比如:李**
- *
- * @param fullName
- * @return
- */
- public static String chineseName(String fullName) {
- if (StringUtils.isBlank(fullName)) {
- return "";
- }
- String name = StringUtils.left(fullName, 1);
- return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
- }
-
- /**
- * 【身份证号】显示第一位和最后一位
- *
- * @param id
- * @return
- */
- public static String idCardNum(String id) {
- if (StringUtils.isBlank(id)) {
- return "";
- }
- return StringUtils.left(id,1).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(id,1), StringUtils.length(id),"*"),"*"));
- }
-
- /**
- * 【虚拟账号】显示第一位和最后一位
- *
- * @param id
- * @return
- */
- public static String account(String id) {
- if (StringUtils.isBlank(id)) {
- return "";
- }
- return StringUtils.left(id,1).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(id,1), StringUtils.length(id),"*"),"*"));
- }
-
-
- /**
- * 【固定电话 后四位,其他隐藏,比如1234
- *
- * @param num
- * @return
- */
- public static String fixedPhone(String num) {
- if (StringUtils.isBlank(num)) {
- return "";
- }
- return StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*");
- }
-
- /**
- * 【手机号码】前三位,后四位,其他隐藏,比如135****6810
- *
- * @param num
- * @return
- */
- public static String mobilePhone(String num) {
- if (StringUtils.isBlank(num)) {
- return "";
- }
- return StringUtils.left(num, 3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*"), "***"));
- }
-
- /**
- * 【地址】只显示到地区,不显示详细地址,比如:北京市海淀区****
- *
- * @param address
- * @param sensitiveSize 敏感信息长度
- * @return
- */
- public static String address(String address, int sensitiveSize) {
- if (StringUtils.isBlank(address)) {
- return "";
- }
- int length = StringUtils.length(address);
- return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length, "*");
- }
-
- /**
- * 【电子邮箱 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com>
- *
- * @param email
- * @return
- */
- public static String email(String email) {
- if (StringUtils.isBlank(email)) {
- return "";
- }
- int index = StringUtils.indexOf(email, "@");
- if (index <= 1)
- return email;
- else
- return StringUtils.rightPad(StringUtils.left(email, 1), index, "*").concat(StringUtils.mid(email, index, StringUtils.length(email)));
- }
-
- /**
- * 【银行卡号】前4位,后3位,其他用星号隐藏每位1个星号,比如:6217 **** **** **** 567>
- *
- * @param cardNum
- * @return
- */
- public static String bankCard(String cardNum) {
- if (StringUtils.isBlank(cardNum)) {
- return "";
- }
- return StringUtils.left(cardNum, 4).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum, 3), StringUtils.length(cardNum), "*"), "****"));
- }
-
- /**
- * 【密码】密码的全部字符都用*代替,比如:******
- *
- * @param password
- * @return
- */
- public static String password(String password) {
- if (StringUtils.isBlank(password)) {
- return "";
- }
- String pwd = StringUtils.left(password, 0);
- return StringUtils.rightPad(pwd, StringUtils.length(password), "*");
- }
-
- /**
- * 遍历List脱敏数据
- * @param content
- * @return
- */
- public static <T> List getList(List<T> content){
- if (content == null || content.size() <= 0) {
- return content;
- }
- List list = new ArrayList<T>();
- for (T t : content) {
- list.add(getObj(t));
- }
- return list;
- }
- }
-
这里我们以UserEntity为例:(一定要序列化)
- package com.artboy.project.model.entity;
-
- import com.artboy.project.common.annotation.Desensitized;
- import com.artboy.project.common.enums.SensitiveTypeEnum;
- import com.baomidou.mybatisplus.annotation.IdType;
- import com.baomidou.mybatisplus.annotation.TableField;
- import com.baomidou.mybatisplus.annotation.TableId;
- import com.baomidou.mybatisplus.annotation.TableLogic;
- import com.baomidou.mybatisplus.annotation.TableName;
- import java.io.Serializable;
- import java.util.Date;
- import lombok.Data;
-
- /**
- * 用户
- *
- * @TableName user
- */
- @TableName(value = "user")
- @Data
- public class UserEntity implements Serializable {
-
- /**
- * id
- */
- @TableId(type = IdType.AUTO)
- private Integer id;
-
- /**
- * 用户昵称
- */
- private String username;
-
- /**
- * 账号
- */
- private String userAccount;
-
- /**
- * 用户头像
- */
- private String avatarUrl;
-
- /**
- * 性别
- */
- private Integer gender;
-
- /**
- * 密码
- */
- @Desensitized(type = SensitiveTypeEnum.PASSWORD)
- private String userPassword;
-
- /**
- * 电话
- */
- @Desensitized(type = SensitiveTypeEnum.MOBILE_PHONE)
- private String phone;
-
- /**
- * 邮箱
- */
- @Desensitized(type = SensitiveTypeEnum.EMAIL)
- private String email;
-
- /**
- * 状态 0 - 正常
- */
- private Integer userStatus;
-
- /**
- * 创建时间,数据库已设置自动更新
- */
- private Date createTime;
-
- /**
- * 更新时间,数据库已设置自动更新
- */
- private Date updateTime;
-
- /**
- * 是否删除
- */
- @TableLogic
- private Integer isDelete;
-
- /**
- * 用户角色 0 - 普通用户 1 - 管理员
- */
- private Integer userRole;
-
- @TableField(exist = false)
- private static final long serialVersionUID = 1L;
- }
测试方法:
- QueryWrapper<UserEntity> queryWrapper = new QueryWrapper<>();
- UserEntity user = userMapper.selectOne(queryWrapper);
- UserEntity safetyUser = DesensitizedUtils.getObj(user);
- package com.fwy.common.config.dataRule;
-
- import lombok.Getter;
-
- /**
- * @description:隐私数据类型枚举
- * @author: xiaYZ
- * @createDate: 2022/6/21
- */
- @Getter
- public enum PrivacyTypeEnum {
-
- /** 自定义(此项需设置脱敏的范围)*/
- CUSTOMER,
-
- /** 姓名 */
- NAME,
-
- /** 身份证号 */
- ID_CARD,
-
- /** 手机号 */
- PHONE,
-
- /** 邮箱 */
- EMAIL,
- }
新建脱敏操作的工具类
- package com.fwy.common.config.dataRule;
-
- /**
- * @description:
- * @author: xiaYZ
- * @createDate: 2022/6/21
- */
- public class PrivacyUtil {
-
- /**
- * 隐藏手机号中间四位
- */
- public static String hidePhone(String phone) {
- return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
- }
-
- /**
- * 隐藏邮箱
- */
- public static String hideEmail(String email) {
- return email.replaceAll("(\\w?)(\\w+)(\\w)(@\\w+\\.[a-z]+(\\.[a-z]+)?)", "$1****$3$4");
- }
-
- /**
- * 隐藏身份证
- */
- public static String hideIDCard(String idCard) {
- return idCard.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1*****$2");
- }
-
- /**
- * 【中文姓名】只显示第一个汉字,其他隐藏为星号,比如:任**
- */
- public static String hideChineseName(String chineseName) {
- if (chineseName == null) {
- return null;
- }
- return desValue(chineseName, 1, 0, "*");
- }
-
- // /**
- // * 【身份证号】显示前4位, 后2位
- // */
- // public static String hideIdCard(String idCard) {
- // return desValue(idCard, 4, 2, "*");
- // }
-
- // /**
- // * 【手机号码】前三位,后四位,其他隐藏。
- // */
- // public static String hidePhone(String phone) {
- // return desValue(phone, 3, 4, "*");
- // }
-
- /**
- * 对字符串进行脱敏操作
- * @param origin 原始字符串
- * @param prefixNoMaskLen 左侧需要保留几位明文字段
- * @param suffixNoMaskLen 右侧需要保留几位明文字段
- * @param maskStr 用于遮罩的字符串, 如'*'
- * @return 脱敏后结果
- */
- public static String desValue(String origin, int prefixNoMaskLen, int suffixNoMaskLen, String maskStr) {
- if (origin == null) {
- return null;
- }
- StringBuilder sb = new StringBuilder();
- for (int i = 0, n = origin.length(); i < n; i++) {
- if (i < prefixNoMaskLen) {
- sb.append(origin.charAt(i));
- continue;
- }
- if (i > (n - suffixNoMaskLen - 1)) {
- sb.append(origin.charAt(i));
- continue;
- }
- sb.append(maskStr);
- }
- return sb.toString();
- }
-
- }
-
- package com.fwy.common.config.dataRule;
-
-
- import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
- import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
-
-
- /**
- * @description: 自定义数据脱敏注解
- * @author: xiaYZ
- * @createDate: 2022/6/21
- */
- @Target(ElementType.FIELD) // 作用在字段上
- @Retention(RetentionPolicy.RUNTIME) // class文件中保留,运行时也保留,能通过反射读取到
- @JacksonAnnotationsInside // 表示自定义自己的注解PrivacyEncrypt
- @JsonSerialize(using = PrivacySerializer.class) // 该注解使用序列化的方式
- public @interface PrivacyEncrypt {
-
- /**
- * 脱敏数据类型(没给默认值,所以使用时必须指定type)
- */
- PrivacyTypeEnum type();
-
- /**
- * 前置不需要打码的长度
- */
- int prefixNoMaskLen() default 1;
-
- /**
- * 后置不需要打码的长度
- */
- int suffixNoMaskLen() default 1;
-
- /**
- * 用什么打码
- */
- String symbol() default "*";
- }
-
- package com.fwy.common.config.dataRule;
-
- import com.fasterxml.jackson.core.JsonGenerator;
- import com.fasterxml.jackson.databind.BeanProperty;
- import com.fasterxml.jackson.databind.JsonMappingException;
- import com.fasterxml.jackson.databind.JsonSerializer;
- import com.fasterxml.jackson.databind.SerializerProvider;
- import com.fasterxml.jackson.databind.ser.ContextualSerializer;
- import lombok.AllArgsConstructor;
- import lombok.NoArgsConstructor;
- import org.apache.commons.lang3.StringUtils;
-
- import java.io.IOException;
- import java.util.Objects;
-
- /**
- * @description:
- * @author: xiaYZ
- * @createDate: 2022/6/21
- */
-
-
- @NoArgsConstructor
- @AllArgsConstructor
- public class PrivacySerializer extends JsonSerializer
implements ContextualSerializer { -
- // 脱敏类型
- private PrivacyTypeEnum privacyTypeEnum;
- // 前几位不脱敏
- private Integer prefixNoMaskLen;
- // 最后几位不脱敏
- private Integer suffixNoMaskLen;
- // 用什么打码
- private String symbol;
-
- @Override
- public void serialize(final String origin, final JsonGenerator jsonGenerator,
- final SerializerProvider serializerProvider) throws IOException {
- if (StringUtils.isNotBlank(origin) && null != privacyTypeEnum) {
- switch (privacyTypeEnum) {
- case CUSTOMER:
- jsonGenerator.writeString(PrivacyUtil.desValue(origin, prefixNoMaskLen, suffixNoMaskLen, symbol));
- break;
- case NAME:
- jsonGenerator.writeString(PrivacyUtil.hideChineseName(origin));
- break;
- case ID_CARD:
- jsonGenerator.writeString(PrivacyUtil.hideIDCard(origin));
- break;
- case PHONE:
- jsonGenerator.writeString(PrivacyUtil.hidePhone(origin));
- break;
- case EMAIL:
- jsonGenerator.writeString(PrivacyUtil.hideEmail(origin));
- break;
- default:
- throw new IllegalArgumentException("unknown privacy type enum " + privacyTypeEnum);
- }
- }
- }
-
- @Override
- public JsonSerializer> createContextual(final SerializerProvider serializerProvider,
- final BeanProperty beanProperty) throws JsonMappingException {
- if (beanProperty != null) {
- if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
- PrivacyEncrypt privacyEncrypt = beanProperty.getAnnotation(PrivacyEncrypt.class);
- if (privacyEncrypt == null) {
- privacyEncrypt = beanProperty.getContextAnnotation(PrivacyEncrypt.class);
- }
- if (privacyEncrypt != null) {
- return new PrivacySerializer(privacyEncrypt.type(), privacyEncrypt.prefixNoMaskLen(),
- privacyEncrypt.suffixNoMaskLen(), privacyEncrypt.symbol());
- }
- }
- return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
- }
- return serializerProvider.findNullValueSerializer(null);
- }
- }
-

基于jackson,通过自定义注解的方式实现数据脱敏
spring-web、spring-boot-starter-web已经集成了jackson相关包,不用添加
- <!--jackson依赖-->
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-annotations</artifactId>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- </dependency>
脱敏注解
DesensitizationJsonSerializer.class: 脱敏序列化类
- @Retention(RetentionPolicy.RUNTIME)
- @JacksonAnnotationsInside
- @JsonSerialize(using = DesensitizationJsonSerializer.class)
- public @interface Desensitization {
-
- Class extends AbstractDesensitization> value();
-
- }
脱敏序列化
- /**
- * 脱敏序列化
- */
- public class DesensitizationJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
-
- private AbstractDesensitization desensitization;
-
- public DesensitizationJsonSerializer() {
- }
-
- public DesensitizationJsonSerializer(AbstractDesensitization desensitization) {
- this.desensitization = desensitization;
- }
-
- @Override
- public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
- jsonGenerator.writeString(desensitization.serialize(s));;
- }
-
-
- @Override
- public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
- JsonSerializer<?> jsonSerializer = null;
- if(null == beanProperty) jsonSerializer = serializerProvider.findNullValueSerializer(beanProperty);
-
- if(!Objects.equals(beanProperty.getType().getRawClass(), String.class))
- jsonSerializer = serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
-
- if(Objects.equals(beanProperty.getType().getRawClass(), String.class)){
- jsonSerializer = setDesensitization(jsonSerializer, beanProperty);
- }
- return jsonSerializer;
- }
-
- /**
- * 设置脱敏
- * @param beanProperty
- * @return
- */
- private JsonSerializer<?> setDesensitization(JsonSerializer<?> jsonSerializer, BeanProperty beanProperty) {
- Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);
-
- if (desensitization == null) desensitization = beanProperty.getContextAnnotation(Desensitization.class);
-
- if (desensitization != null) {
- //设置脱敏实例
- try {
- jsonSerializer = new DesensitizationJsonSerializer(desensitization.value().newInstance());
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
- return jsonSerializer;
- }
- }
脱敏类
脱敏父类
子类通过继承AbstractDesensitization实现扩展
- public abstract class AbstractDesensitization {
-
- /**
- * 脱敏
- * @param value
- * @return
- */
- public abstract String serialize(String value);
-
- }
中文姓名脱敏
- public class ChineseNameDesensitization extends AbstractDesensitization {
-
- @Override
- public String serialize(String value) {
- String serializeValue = "";
- if(value.length() < 3){
- serializeValue = value.replaceAll(".*(?=[\\u4e00-\\u9fa5])","*");
- }else{
- serializeValue = value.replaceAll("(?<=[\\u4e00-\\u9fa5]).*(?=[\\u4e00-\\u9fa5])","*");
- }
- return serializeValue;
- }
-
- }
手机号脱敏
- public class MobilePhoneDesensitization extends AbstractDesensitization {
-
- @Override
- public String serialize(String value) {
- return value.replaceAll("(\\d{3})\\d{4}(\\d{4})","$1****$2");
- }
-
- }
身份证脱敏
- public class IdCardDesensitization extends AbstractDesensitization {
-
- @Override
- public String serialize(String value) {
- return value.replaceAll("(?<=\\w{3})\\w(?=\\w{4})","*");
- }
-
- }
测试
添加脱敏注解
- public class User {
-
- @Desensitization(ChineseNameDesensitization.class)
- private String name;
-
- private Integer age;
-
- @Desensitization(IdCardDesensitization.class)
- private String idCard;
-
- @Desensitization(MobilePhoneDesensitization.class)
- private String mobilePhone;
-
- //...get and set
- }
新建UserController,查询用户信息
- @RestController
- public class UserController {
-
- @GetMapping("/users")
- private List<User> users() throws Exception {
- List<User> girls = new ArrayList<>();
- User user = new User();
- user.setName("西施");
- user.setAge(18);
- user.setIdCard("123456789123456202");
- user.setMobilePhone("12345678901");
- User user2 = new User();
- user2.setName("杨贵妃");
- user2.setAge(18);
- user2.setIdCard("123456789123456202");
- user2.setMobilePhone("12345678901");
- User user3 = new User();
- user3.setName("古代四大美女之一 * 貂蝉");
- user3.setAge(18);
- user3.setIdCard("123456789123456202");
- user3.setMobilePhone("12345678901");
- User user4 = new User();
- user4.setName("古代四大美女之一 * 王昭君");
- user4.setAge(18);
- user4.setIdCard("123456789123456202");
- user4.setMobilePhone("12345678901");
- User user5 = new User();
- user5.setName(null);
- user5.setAge(18);
- user5.setIdCard(null);
- user5.setMobilePhone(null);
- girls.add(user);
- girls.add(user2);
- girls.add(user3);
- girls.add(user4);
- girls.add(user5);
- return girls;
- }
- }