数据脱敏又称数据去隐私化或数据变形,是在给定的规则、策略下对敏感数据进行变换、修改的技术机制,能够在很大程度上解决敏感数据在非可信环境中使用的问题。根据数据保护规范和脱敏策略.对业务数据中的敏感信息实施自动变形.实现对敏感信息的隐藏。

项目是在controller层进行脱敏,查阅google和github有两种较为方便的方法

  1. 一种是基于注解 desensitized基于注解的形式进行脱敏 GitHub
  2. 一种是基于框架本质上还是注解,但是已经封装好了,当然还提供fastjson的方式进行脱敏 sensitive框架进行脱敏 GitHub
    由于项目原因不想引入框架,所以使用基于注解的方式

新建枚举类型,标明是何种脱敏方式

  1. package com.blgroup.vision.common.enums;
  2. /**
  3. * @program: blgroup-scloud-web
  4. * @description: 脱敏枚举
  5. * @author: ingxx
  6. * @create: 2019-12-17 14:32
  7. **/
  8. public enum SensitiveTypeEnum {
  9. /**
  10. * 中文名
  11. */
  12. CHINESE_NAME,
  13. /**
  14. * 身份证号
  15. */
  16. ID_CARD,
  17. /**
  18. * 座机号
  19. */
  20. FIXED_PHONE,
  21. /**
  22. * 手机号
  23. */
  24. MOBILE_PHONE,
  25. /**
  26. * 地址
  27. */
  28. ADDRESS,
  29. /**
  30. * 电子邮件
  31. */
  32. EMAIL,
  33. /**
  34. * 银行卡
  35. */
  36. BANK_CARD,
  37. /**
  38. * 虚拟账号
  39. */
  40. ACCOUNT,
  41. /**
  42. * 密码
  43. */
  44. PASSWORD;
  45. }

创建注解,标识需要脱敏的字段

  1. package com.blgroup.vision.common.annotation;
  2. import com.blgroup.vision.common.enums.SensitiveTypeEnum;
  3. import java.lang.annotation.*;
  4. /**
  5. * @program: blgroup-scloud-web
  6. * @description: 脱敏注解
  7. * @author: ingxx
  8. * @create: 2019-12-17 14:32
  9. **/
  10. @Target({ElementType.FIELD, ElementType.METHOD})
  11. @Retention(RetentionPolicy.RUNTIME)
  12. @Inherited
  13. @Documented
  14. public @interface Desensitized {
  15. /*脱敏类型(规则)*/
  16. SensitiveTypeEnum type();
  17. /*判断注解是否生效的方法*/
  18. String isEffictiveMethod() default "";
  19. }

创建Object工具类,用于复制对象和对对象的其他操作,注意使用fastjson实现深拷贝对于复杂的对象会出现栈溢出,本人测试是发生在转换对象时对象会转换成JsonObject对象,在getHash方法中出现递归调用导致栈溢出,具体原因不明

  1. package com.blgroup.vision.common.utils;
  2. import com.alibaba.fastjson.JSON;
  3. import org.apache.commons.lang3.StringUtils;
  4. import java.io.*;
  5. import java.lang.reflect.Array;
  6. import java.lang.reflect.Field;
  7. import java.lang.reflect.InvocationTargetException;
  8. import java.lang.reflect.Modifier;
  9. import java.util.*;
  10. /**
  11. * @program: blgroup-scloud-web
  12. * @description: 脱敏Object工具类
  13. * @author: ingxx
  14. * @create: 2019-12-17 14:32
  15. **/
  16. public class DesensitizedObjectUtils {
  17. /**
  18. * 用序列化-反序列化方式实现深克隆
  19. * 缺点:1、被拷贝的对象必须要实现序列化
  20. *
  21. * @param obj
  22. * @return
  23. */
  24. @SuppressWarnings("unchecked")
  25. public static <T> T deepCloneObject(T obj) {
  26. T t = (T) new Object();
  27. try {
  28. ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
  29. ObjectOutputStream out = new ObjectOutputStream(byteOut);
  30. out.writeObject(obj);
  31. out.close();
  32. ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
  33. ObjectInputStream in = new ObjectInputStream(byteIn);
  34. t = (T) in.readObject();
  35. in.close();
  36. } catch (IOException e) {
  37. e.printStackTrace();
  38. } catch (ClassNotFoundException e) {
  39. e.printStackTrace();
  40. }
  41. return t;
  42. }
  43. /**
  44. * 用序列化-反序列化的方式实现深克隆
  45. * 缺点:1、当实体中存在接口类型的参数,并且这个接口指向的实例为枚举类型时,会报错"com.alibaba.fastjson.JSONException: syntax error, expect {, actual string, pos 171, fieldName iLimitKey"
  46. *
  47. * @param objSource
  48. * @return
  49. */
  50. public static Object deepCloneByFastJson(Object objSource) {
  51. String tempJson = JSON.toJSONString(objSource);
  52. Object clone = JSON.parseObject(tempJson, objSource.getClass());
  53. return clone;
  54. }
  55. /**
  56. * 深度克隆对象
  57. *
  58. * @throws IllegalAccessException
  59. * @throws InstantiationException
  60. */
  61. public static Object deepClone(Object objSource) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
  62. if (null == objSource) return null;
  63. //是否jdk类型、基础类型、枚举类型
  64. if (isJDKType(objSource.getClass())
  65. || objSource.getClass().isPrimitive()
  66. || objSource instanceof Enum<?>) {
  67. if ("java.lang.String".equals(objSource.getClass().getName())) {//目前只支持String类型深复制
  68. return new String((String) objSource);
  69. } else {
  70. return objSource;
  71. }
  72. }
  73. // 获取源对象类型
  74. Class<?> clazz = objSource.getClass();
  75. Object objDes = clazz.newInstance();
  76. // 获得源对象所有属性
  77. Field[] fields = getAllFields(objSource);
  78. // 循环遍历字段,获取字段对应的属性值
  79. for (Field field : fields) {
  80. field.setAccessible(true);
  81. if (null == field) continue;
  82. Object value = field.get(objSource);
  83. if (null == value) continue;
  84. Class<?> type = value.getClass();
  85. if (isStaticFinal(field)) {
  86. continue;
  87. }
  88. try {
  89. //遍历集合属性
  90. if (type.isArray()) {//对数组类型的字段进行递归过滤
  91. int len = Array.getLength(value);
  92. if (len < 1) continue;
  93. Class<?> c = value.getClass().getComponentType();
  94. Array newArray = (Array) Array.newInstance(c, len);
  95. for (int i = 0; i < len; i++) {
  96. Object arrayObject = Array.get(value, i);
  97. Array.set(newArray, i, deepClone(arrayObject));
  98. }
  99. } else if (value instanceof Collection<?>) {
  100. Collection newCollection = (Collection) value.getClass().newInstance();
  101. Collection<?> c = (Collection<?>) value;
  102. Iterator<?> it = c.iterator();
  103. while (it.hasNext()) {
  104. Object collectionObj = it.next();
  105. newCollection.add(deepClone(collectionObj));
  106. }
  107. field.set(objDes, newCollection);
  108. continue;
  109. } else if (value instanceof Map<?, ?>) {
  110. Map newMap = (Map) value.getClass().newInstance();
  111. Map<?, ?> m = (Map<?, ?>) value;
  112. Set<?> set = m.entrySet();
  113. for (Object o : set) {
  114. Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
  115. Object mapVal = entry.getValue();
  116. newMap.put(entry.getKey(), deepClone(mapVal));
  117. }
  118. field.set(objDes, newMap);
  119. continue;
  120. }
  121. //是否jdk类型或基础类型
  122. if (isJDKType(field.get(objSource).getClass())
  123. || field.getClass().isPrimitive()
  124. || isStaticType(field)
  125. || value instanceof Enum<?>) {
  126. if ("java.lang.String".equals(value.getClass().getName())) {//目前只支持String类型深复制
  127. field.set(objDes, new String((String) value));
  128. } else {
  129. field.set(objDes, field.get(objSource));
  130. }
  131. continue;
  132. }
  133. //是否枚举
  134. if (value.getClass().isEnum()) {
  135. field.set(objDes, field.get(objSource));
  136. continue;
  137. }
  138. //是否自定义类
  139. if (isUserDefinedType(value.getClass())) {
  140. field.set(objDes, deepClone(value));
  141. continue;
  142. }
  143. } catch (Exception e) {
  144. e.printStackTrace();
  145. }
  146. }
  147. return objDes;
  148. }
  149. /**
  150. * 是否静态变量
  151. *
  152. * @param field
  153. * @return
  154. */
  155. private static boolean isStaticType(Field field) {
  156. return field.getModifiers() == 8 ? true : false;
  157. }
  158. private static boolean isStaticFinal(Field field) {
  159. return Modifier.isFinal(field.getModifiers()) && Modifier.isStatic(field.getModifiers());
  160. }
  161. /**
  162. * 是否jdk类型变量
  163. *
  164. * @param clazz
  165. * @return
  166. * @throws IllegalAccessException
  167. */
  168. private static boolean isJDKType(Class clazz) throws IllegalAccessException {
  169. //Class clazz = field.get(objSource).getClass();
  170. return org.apache.commons.lang3.StringUtils.startsWith(clazz.getPackage().getName(), "javax.")
  171. || org.apache.commons.lang3.StringUtils.startsWith(clazz.getPackage().getName(), "java.")
  172. || org.apache.commons.lang3.StringUtils.startsWith(clazz.getName(), "javax.")
  173. || org.apache.commons.lang3.StringUtils.startsWith(clazz.getName(), "java.");
  174. }
  175. /**
  176. * 是否用户自定义类型
  177. *
  178. * @param clazz
  179. * @return
  180. */
  181. private static boolean isUserDefinedType(Class<?> clazz) {
  182. return
  183. clazz.getPackage() != null
  184. && !org.apache.commons.lang3.StringUtils.startsWith(clazz.getPackage().getName(), "javax.")
  185. && !org.apache.commons.lang3.StringUtils.startsWith(clazz.getPackage().getName(), "java.")
  186. && !org.apache.commons.lang3.StringUtils.startsWith(clazz.getName(), "javax.")
  187. && !StringUtils.startsWith(clazz.getName(), "java.");
  188. }
  189. /**
  190. * 获取包括父类所有的属性
  191. *
  192. * @param objSource
  193. * @return
  194. */
  195. public static Field[] getAllFields(Object objSource) {
  196. /*获得当前类的所有属性(private、protected、public)*/
  197. List<Field> fieldList = new ArrayList<Field>();
  198. Class tempClass = objSource.getClass();
  199. while (tempClass != null && !tempClass.getName().toLowerCase().equals("java.lang.object")) {//当父类为null的时候说明到达了最上层的父类(Object类).
  200. fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields()));
  201. tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
  202. }
  203. Field[] fields = new Field[fieldList.size()];
  204. fieldList.toArray(fields);
  205. return fields;
  206. }
  207. /**
  208. * 深度克隆对象
  209. *
  210. * @throws IllegalAccessException
  211. * @throws InstantiationException
  212. */
  213. @Deprecated
  214. public static Object copy(Object objSource) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
  215. if (null == objSource) return null;
  216. // 获取源对象类型
  217. Class<?> clazz = objSource.getClass();
  218. Object objDes = clazz.newInstance();
  219. // 获得源对象所有属性
  220. Field[] fields = getAllFields(objSource);
  221. // 循环遍历字段,获取字段对应的属性值
  222. for (Field field : fields) {
  223. field.setAccessible(true);
  224. // 如果该字段是 static + final 修饰
  225. if (field.getModifiers() >= 24) {
  226. continue;
  227. }
  228. try {
  229. // 设置字段可见,即可用get方法获取属性值。
  230. field.set(objDes, field.get(objSource));
  231. } catch (Exception e) {
  232. e.printStackTrace();
  233. }
  234. }
  235. return objDes;
  236. }
  237. }

创建脱敏工具类

  1. package com.blgroup.vision.common.utils;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.serializer.SerializerFeature;
  4. import com.blgroup.vision.common.annotation.Desensitized;
  5. import org.apache.commons.lang3.StringUtils;
  6. import java.lang.reflect.Array;
  7. import java.lang.reflect.Field;
  8. import java.lang.reflect.InvocationTargetException;
  9. import java.lang.reflect.Method;
  10. import java.util.*;
  11. /**
  12. * @program: blgroup-scloud-web
  13. * @description: 脱敏工具类
  14. * @author: ingxx
  15. * @create: 2019-12-17 14:32
  16. **/
  17. public class DesensitizedUtils{
  18. /**
  19. * 获取脱敏json串(递归引用会导致java.lang.StackOverflowError)
  20. *
  21. * @param javaBean
  22. * @return
  23. */
  24. public static String getJson(Object javaBean) {
  25. String json = null;
  26. if (null != javaBean) {
  27. try {
  28. if (javaBean.getClass().isInterface()) return json;
  29. /* 克隆出一个实体进行字段修改,避免修改原实体 */
  30. //Object clone =DesensitizedObjectUtils.deepCloneObject(javaBean);
  31. //Object clone =DesensitizedObjectUtils.deepCloneByFastJson(javaBean);
  32. Object clone = DesensitizedObjectUtils.deepClone(javaBean);
  33. /* 定义一个计数器,用于避免重复循环自定义对象类型的字段 */
  34. Set<Integer> referenceCounter = new HashSet<Integer>();
  35. /* 对克隆实体进行脱敏操作 */
  36. DesensitizedUtils.replace(DesensitizedObjectUtils.getAllFields(clone), clone, referenceCounter);
  37. /* 利用fastjson对脱敏后的克隆对象进行序列化 */
  38. json = JSON.toJSONString(clone, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty);
  39. /* 清空计数器 */
  40. referenceCounter.clear();
  41. referenceCounter = null;
  42. } catch (Throwable e) {
  43. e.printStackTrace();
  44. }
  45. }
  46. return json;
  47. }
  48. public static <T> T getObj(T javaBean) {
  49. T clone = null;
  50. if (null != javaBean) {
  51. try {
  52. if (javaBean.getClass().isInterface()) return null;
  53. /* 克隆出一个实体进行字段修改,避免修改原实体 */
  54. //Object clone =DesensitizedObjectUtils.deepCloneObject(javaBean);
  55. //Object clone =DesensitizedObjectUtils.deepCloneByFastJson(javaBean);
  56. clone = (T) DesensitizedObjectUtils.deepClone(javaBean);
  57. /* 定义一个计数器,用于避免重复循环自定义对象类型的字段 */
  58. Set<Integer> referenceCounter = new HashSet<Integer>();
  59. /* 对克隆实体进行脱敏操作 */
  60. DesensitizedUtils.replace(DesensitizedObjectUtils.getAllFields(clone), clone, referenceCounter);
  61. /* 清空计数器 */
  62. referenceCounter.clear();
  63. referenceCounter = null;
  64. } catch (Throwable e) {
  65. e.printStackTrace();
  66. }
  67. }
  68. return clone;
  69. }
  70. /**
  71. * 对需要脱敏的字段进行转化
  72. *
  73. * @param fields
  74. * @param javaBean
  75. * @param referenceCounter
  76. * @throws IllegalArgumentException
  77. * @throws IllegalAccessException
  78. */
  79. private static void replace(Field[] fields, Object javaBean, Set<Integer> referenceCounter) throws IllegalArgumentException, IllegalAccessException {
  80. if (null != fields && fields.length > 0) {
  81. for (Field field : fields) {
  82. field.setAccessible(true);
  83. if (null != field && null != javaBean) {
  84. Object value = field.get(javaBean);
  85. if (null != value) {
  86. Class<?> type = value.getClass();
  87. //处理子属性,包括集合中的
  88. if (type.isArray()) {//对数组类型的字段进行递归过滤
  89. int len = Array.getLength(value);
  90. for (int i = 0; i < len; i++) {
  91. Object arrayObject = Array.get(value, i);
  92. if (isNotGeneralType(arrayObject.getClass(), arrayObject, referenceCounter)) {
  93. replace(DesensitizedObjectUtils.getAllFields(arrayObject), arrayObject, referenceCounter);
  94. }
  95. }
  96. } else if (value instanceof Collection<?>) {//对集合类型的字段进行递归过滤
  97. Collection<?> c = (Collection<?>) value;
  98. Iterator<?> it = c.iterator();
  99. while (it.hasNext()) {// TODO: 待优化
  100. Object collectionObj = it.next();
  101. if (isNotGeneralType(collectionObj.getClass(), collectionObj, referenceCounter)) {
  102. replace(DesensitizedObjectUtils.getAllFields(collectionObj), collectionObj, referenceCounter);
  103. }
  104. }
  105. } else if (value instanceof Map<?, ?>) {//对Map类型的字段进行递归过滤
  106. Map<?, ?> m = (Map<?, ?>) value;
  107. Set<?> set = m.entrySet();
  108. for (Object o : set) {
  109. Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
  110. Object mapVal = entry.getValue();
  111. if (isNotGeneralType(mapVal.getClass(), mapVal, referenceCounter)) {
  112. replace(DesensitizedObjectUtils.getAllFields(mapVal), mapVal, referenceCounter);
  113. }
  114. }
  115. } else if (value instanceof Enum<?>) {
  116. continue;
  117. }
  118. /*除基础类型、jdk类型的字段之外,对其他类型的字段进行递归过滤*/
  119. else {
  120. if (!type.isPrimitive()
  121. && type.getPackage() != null
  122. && !StringUtils.startsWith(type.getPackage().getName(), "javax.")
  123. && !StringUtils.startsWith(type.getPackage().getName(), "java.")
  124. && !StringUtils.startsWith(field.getType().getName(), "javax.")
  125. && !StringUtils.startsWith(field.getName(), "java.")
  126. && referenceCounter.add(value.hashCode())) {
  127. replace(DesensitizedObjectUtils.getAllFields(value), value, referenceCounter);
  128. }
  129. }
  130. }
  131. //脱敏操作
  132. setNewValueForField(javaBean, field, value);
  133. }
  134. }
  135. }
  136. }
  137. /**
  138. * 排除基础类型、jdk类型、枚举类型的字段
  139. *
  140. * @param clazz
  141. * @param value
  142. * @param referenceCounter
  143. * @return
  144. */
  145. private static boolean isNotGeneralType(Class<?> clazz, Object value, Set<Integer> referenceCounter) {
  146. return !clazz.isPrimitive()
  147. && clazz.getPackage() != null
  148. && !clazz.isEnum()
  149. && !StringUtils.startsWith(clazz.getPackage().getName(), "javax.")
  150. && !StringUtils.startsWith(clazz.getPackage().getName(), "java.")
  151. && !StringUtils.startsWith(clazz.getName(), "javax.")
  152. && !StringUtils.startsWith(clazz.getName(), "java.")
  153. && referenceCounter.add(value.hashCode());
  154. }
  155. /**
  156. * 脱敏操作(按照规则转化需要脱敏的字段并设置新值)
  157. * 目前只支持String类型的字段,如需要其他类型如BigDecimal、Date等类型,可以添加
  158. *
  159. * @param javaBean
  160. * @param field
  161. * @param value
  162. * @throws IllegalAccessException
  163. */
  164. public static void setNewValueForField(Object javaBean, Field field, Object value) throws IllegalAccessException {
  165. //处理自身的属性
  166. Desensitized annotation = field.getAnnotation(Desensitized.class);
  167. if (field.getType().equals(String.class) && null != annotation && executeIsEffictiveMethod(javaBean, annotation)) {
  168. String valueStr = (String) value;
  169. if (StringUtils.isNotBlank(valueStr)) {
  170. switch (annotation.type()) {
  171. case CHINESE_NAME: {
  172. field.set(javaBean, DesensitizedUtils.chineseName(valueStr));
  173. break;
  174. }
  175. case ID_CARD: {
  176. field.set(javaBean, DesensitizedUtils.idCardNum(valueStr));
  177. break;
  178. }
  179. case FIXED_PHONE: {
  180. field.set(javaBean, DesensitizedUtils.fixedPhone(valueStr));
  181. break;
  182. }
  183. case MOBILE_PHONE: {
  184. field.set(javaBean, DesensitizedUtils.mobilePhone(valueStr));
  185. break;
  186. }
  187. case ADDRESS: {
  188. field.set(javaBean, DesensitizedUtils.address(valueStr, 7));
  189. break;
  190. }
  191. case EMAIL: {
  192. field.set(javaBean, DesensitizedUtils.email(valueStr));
  193. break;
  194. }
  195. case BANK_CARD: {
  196. field.set(javaBean, DesensitizedUtils.bankCard(valueStr));
  197. break;
  198. }
  199. case PASSWORD: {
  200. field.set(javaBean, DesensitizedUtils.password(valueStr));
  201. break;
  202. }case ACCOUNT:{
  203. field.set(javaBean, DesensitizedUtils.account(valueStr));
  204. break;
  205. }
  206. }
  207. }
  208. }
  209. }
  210. /**
  211. * 执行某个对象中指定的方法
  212. *
  213. * @param javaBean 对象
  214. * @param desensitized
  215. * @return
  216. */
  217. private static boolean executeIsEffictiveMethod(Object javaBean, Desensitized desensitized) {
  218. boolean isAnnotationEffictive = true;//注解默认生效
  219. if (desensitized != null) {
  220. String isEffictiveMethod = desensitized.isEffictiveMethod();
  221. if (isNotEmpty(isEffictiveMethod)) {
  222. try {
  223. Method method = javaBean.getClass().getMethod(isEffictiveMethod);
  224. method.setAccessible(true);
  225. isAnnotationEffictive = (Boolean) method.invoke(javaBean);
  226. } catch (NoSuchMethodException e) {
  227. e.printStackTrace();
  228. } catch (IllegalAccessException e) {
  229. e.printStackTrace();
  230. } catch (InvocationTargetException e) {
  231. e.printStackTrace();
  232. }
  233. }
  234. }
  235. return isAnnotationEffictive;
  236. }
  237. private static boolean isNotEmpty(String str) {
  238. return str != null && !"".equals(str);
  239. }
  240. private static boolean isEmpty(String str) {
  241. return !isNotEmpty(str);
  242. }
  243. /**
  244. * 【中文姓名】只显示第一个汉字,其他隐藏为2个星号,比如:李**
  245. *
  246. * @param fullName
  247. * @return
  248. */
  249. public static String chineseName(String fullName) {
  250. if (StringUtils.isBlank(fullName)) {
  251. return "";
  252. }
  253. String name = StringUtils.left(fullName, 1);
  254. return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
  255. }
  256. /**
  257. * 【身份证号】显示第一位和最后一位
  258. *
  259. * @param id
  260. * @return
  261. */
  262. public static String idCardNum(String id) {
  263. if (StringUtils.isBlank(id)) {
  264. return "";
  265. }
  266. return StringUtils.left(id,1).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(id,1), StringUtils.length(id),"*"),"*"));
  267. }
  268. /**
  269. * 【虚拟账号】显示第一位和最后一位
  270. *
  271. * @param id
  272. * @return
  273. */
  274. public static String account(String id) {
  275. if (StringUtils.isBlank(id)) {
  276. return "";
  277. }
  278. return StringUtils.left(id,1).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(id,1), StringUtils.length(id),"*"),"*"));
  279. }
  280. /**
  281. * 【固定电话 后四位,其他隐藏,比如1234
  282. *
  283. * @param num
  284. * @return
  285. */
  286. public static String fixedPhone(String num) {
  287. if (StringUtils.isBlank(num)) {
  288. return "";
  289. }
  290. return StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*");
  291. }
  292. /**
  293. * 【手机号码】前三位,后四位,其他隐藏,比如135****6810
  294. *
  295. * @param num
  296. * @return
  297. */
  298. public static String mobilePhone(String num) {
  299. if (StringUtils.isBlank(num)) {
  300. return "";
  301. }
  302. return StringUtils.left(num, 3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*"), "***"));
  303. }
  304. /**
  305. * 【地址】只显示到地区,不显示详细地址,比如:北京市海淀区****
  306. *
  307. * @param address
  308. * @param sensitiveSize 敏感信息长度
  309. * @return
  310. */
  311. public static String address(String address, int sensitiveSize) {
  312. if (StringUtils.isBlank(address)) {
  313. return "";
  314. }
  315. int length = StringUtils.length(address);
  316. return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length, "*");
  317. }
  318. /**
  319. * 【电子邮箱 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com>
  320. *
  321. * @param email
  322. * @return
  323. */
  324. public static String email(String email) {
  325. if (StringUtils.isBlank(email)) {
  326. return "";
  327. }
  328. int index = StringUtils.indexOf(email, "@");
  329. if (index <= 1)
  330. return email;
  331. else
  332. return StringUtils.rightPad(StringUtils.left(email, 1), index, "*").concat(StringUtils.mid(email, index, StringUtils.length(email)));
  333. }
  334. /**
  335. * 【银行卡号】前4位,后3位,其他用星号隐藏每位1个星号,比如:6217 **** **** **** 567>
  336. *
  337. * @param cardNum
  338. * @return
  339. */
  340. public static String bankCard(String cardNum) {
  341. if (StringUtils.isBlank(cardNum)) {
  342. return "";
  343. }
  344. return StringUtils.left(cardNum, 4).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum, 3), StringUtils.length(cardNum), "*"), "****"));
  345. }
  346. /**
  347. * 【密码】密码的全部字符都用*代替,比如:******
  348. *
  349. * @param password
  350. * @return
  351. */
  352. public static String password(String password) {
  353. if (StringUtils.isBlank(password)) {
  354. return "";
  355. }
  356. String pwd = StringUtils.left(password, 0);
  357. return StringUtils.rightPad(pwd, StringUtils.length(password), "*");
  358. }
  359. /**
  360. * 遍历List脱敏数据
  361. * @param content
  362. * @return
  363. */
  364. public static <T> List getList(List<T> content){
  365. if (content == null || content.size() <= 0) {
  366. return content;
  367. }
  368. List list = new ArrayList<T>();
  369. for (T t : content) {
  370. list.add(getObj(t));
  371. }
  372. return list;
  373. }
  374. }

因生产项目中不能展示 所以采用Demo的形式

创建测试实体类

  1. package top.ingxx.pojo;
  2. import lombok.Data;
  3. import top.ingxx.annotation.Desensitized;
  4. import top.ingxx.enums.SensitiveTypeEnum;
  5. @Data
  6. public class UserInfo {
  7. @Desensitized(type = SensitiveTypeEnum.CHINESE_NAME)
  8. private String realName;
  9. @Desensitized(type = SensitiveTypeEnum.ID_CARD)
  10. private String idCardNo;
  11. @Desensitized(type = SensitiveTypeEnum.MOBILE_PHONE)
  12. private String mobileNo;
  13. @Desensitized(type = SensitiveTypeEnum.ADDRESS)
  14. private String address;
  15. @Desensitized(type = SensitiveTypeEnum.PASSWORD)
  16. private String password;
  17. @Desensitized(type = SensitiveTypeEnum.BANK_CARD)
  18. private String bankCard;
  19. }

测试实体字段

  1. package top.ingxx.pojo;
  2. import lombok.Data;
  3. /**
  4. * @program: demo
  5. * @description: 测试
  6. * @author: ingxx
  7. * @create: 2019-12-18 09:16
  8. **/
  9. @Data
  10. public class Test {
  11. private UserInfo userInfo;
  12. }

测试主方法

  1. import com.alibaba.fastjson.JSON;
  2. import top.ingxx.pojo.Test;
  3. import top.ingxx.pojo.UserInfo;
  4. import top.ingxx.utils.DesensitizedUtils;
  5. public class Main {
  6. public static void main(String[] args) {
  7. UserInfo userInfo = new UserInfo();
  8. userInfo.setAddress("上海浦东新区未知地址12号");
  9. userInfo.setIdCardNo("111521444474441411");
  10. userInfo.setMobileNo("13555551141");
  11. userInfo.setPassword("123456");
  12. userInfo.setRealName("张三");
  13. userInfo.setBankCard("111521444474441411");
  14. Test test = new Test();
  15. test.setUserInfo(userInfo);
  16. System.out.println(JSON.toJSONString(test));
  17. Test obj = DesensitizedUtils.getObj(test);
  18. System.out.println(JSON.toJSONString(obj));
  19. }
  20. }

使用时可以对单个字段脱敏 也可以直接使用 getObj getJson getList等方法

版权声明:本文为ingxx原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/ingxx/p/12059235.html