基于注解的字段脱敏处理

java 中对自定义注解的说明请参见:

http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html

http://www.cnblogs.com/peida/archive/2013/04/26/3038503.html

有这样一个场景,系统中可以出现敏感的数据,在打印日志的时候,我们并不希望打印出现,这样,我们使用自己定义注解,来解决这个问题。

定义需要脱敏的字段规则。

  1. import java.lang.reflect.Array;
  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.Method;
  4. import java.util.Collection;
  5. import java.util.HashSet;
  6. import java.util.Iterator;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9. import java.util.Set;
  10. import org.apache.commons.lang.ArrayUtils;
  11. import org.apache.commons.lang.StringUtils;
  12. import com.alibaba.fastjson.JSON;
  13. import com.alibaba.fastjson.serializer.SerializerFeature;
  14. import com.google.gson.Gson;
  15. import com.ucf.platform.framework.core.annotation.SensitiveInfo;
  16. import com.ucf.platform.framework.core.log.UcfLogger;
  17. import com.ucf.platform.framework.core.log.UcfLoggerFactory;
  18. /**
  19. * @Title: SensitiveInfoUtils.java
  20. * @Copyright: Copyright (c) 2011
  21. * @Description: <br>
  22. * 敏感信息屏蔽工具<br>
  23. */
  24. public final class SensitiveInfoUtils {
  25. private final static UcfLogger logger = UcfLoggerFactory.getLogger(SensitiveInfoUtils.class);
  26. /**
  27. * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
  28. *
  29. * @param name
  30. * @return
  31. */
  32. public static String chineseName(String fullName) {
  33. if (StringUtils.isBlank(fullName)) {
  34. return "";
  35. }
  36. String name = StringUtils.left(fullName, 1);
  37. return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
  38. }
  39. /**
  40. * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
  41. *
  42. * @param familyName
  43. * @param givenName
  44. * @return
  45. */
  46. public static String chineseName(String familyName, String givenName) {
  47. if (StringUtils.isBlank(familyName) || StringUtils.isBlank(givenName)) {
  48. return "";
  49. }
  50. return chineseName(familyName + givenName);
  51. }
  52. /**
  53. * [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
  54. *
  55. * @param id
  56. * @return
  57. */
  58. public static String idCardNum(String id) {
  59. if (StringUtils.isBlank(id)) {
  60. return "";
  61. }
  62. String num = StringUtils.right(id, 4);
  63. return StringUtils.leftPad(num, StringUtils.length(id), "*");
  64. }
  65. /**
  66. * [固定电话] 后四位,其他隐藏<例子:****1234>
  67. *
  68. * @param num
  69. * @return
  70. */
  71. public static String fixedPhone(String num) {
  72. if (StringUtils.isBlank(num)) {
  73. return "";
  74. }
  75. return StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*");
  76. }
  77. /**
  78. * [手机号码] 前三位,后四位,其他隐藏<例子:138******1234>
  79. *
  80. * @param num
  81. * @return
  82. */
  83. public static String mobilePhone(String num) {
  84. if (StringUtils.isBlank(num)) {
  85. return "";
  86. }
  87. return StringUtils.left(num, 3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*"), "***"));
  88. }
  89. /**
  90. * [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****>
  91. *
  92. * @param address
  93. * @param sensitiveSize
  94. * 敏感信息长度
  95. * @return
  96. */
  97. public static String address(String address, int sensitiveSize) {
  98. if (StringUtils.isBlank(address)) {
  99. return "";
  100. }
  101. int length = StringUtils.length(address);
  102. return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length, "*");
  103. }
  104. /**
  105. * [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com>
  106. *
  107. * @param email
  108. * @return
  109. */
  110. public static String email(String email) {
  111. if (StringUtils.isBlank(email)) {
  112. return "";
  113. }
  114. int index = StringUtils.indexOf(email, "@");
  115. if (index <= 1)
  116. return email;
  117. else
  118. return StringUtils.rightPad(StringUtils.left(email, 1), index, "*").concat(StringUtils.mid(email, index, StringUtils.length(email)));
  119. }
  120. /**
  121. * [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234>
  122. *
  123. * @param cardNum
  124. * @return
  125. */
  126. public static String bankCard(String cardNum) {
  127. if (StringUtils.isBlank(cardNum)) {
  128. return "";
  129. }
  130. return StringUtils.left(cardNum, 6).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum, 4), StringUtils.length(cardNum), "*"), "******"));
  131. }
  132. /**
  133. * [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号<例子:12********>
  134. *
  135. * @param code
  136. * @return
  137. */
  138. public static String cnapsCode(String code) {
  139. if (StringUtils.isBlank(code)) {
  140. return "";
  141. }
  142. return StringUtils.rightPad(StringUtils.left(code, 2), StringUtils.length(code), "*");
  143. }
  144. /**
  145. * 获取脱敏json串 <注意:递归引用会导致java.lang.StackOverflowError>
  146. *
  147. * @param javaBean
  148. * @return
  149. */
  150. public static String getJson(Object javaBean) {
  151. String json = null;
  152. if (null != javaBean) {
  153. Class<? extends Object> raw = javaBean.getClass();
  154. try {
  155. if (raw.isInterface())
  156. return json;
  157. Gson g = new Gson();
  158. Object clone = g.fromJson(g.toJson(javaBean, javaBean.getClass()), javaBean.getClass());
  159. Set<Integer> referenceCounter = new HashSet<Integer>();
  160. SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(raw), clone, referenceCounter);
  161. json = JSON.toJSONString(clone, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty);
  162. referenceCounter.clear();
  163. referenceCounter = null;
  164. } catch (Throwable e) {
  165. logger.error("SensitiveInfoUtils.getJson() ERROR", e);
  166. }
  167. }
  168. return json;
  169. }
  170. private static Field[] findAllField(Class<?> clazz) {
  171. Field[] fileds = clazz.getDeclaredFields();
  172. while (null != clazz.getSuperclass() && !Object.class.equals(clazz.getSuperclass())) {
  173. fileds = (Field[]) ArrayUtils.addAll(fileds, clazz.getSuperclass().getDeclaredFields());
  174. clazz = clazz.getSuperclass();
  175. }
  176. return fileds;
  177. }
  178. private static void replace(Field[] fields, Object javaBean, Set<Integer> referenceCounter) throws IllegalArgumentException, IllegalAccessException {
  179. if (null != fields && fields.length > 0) {
  180. for (Field field : fields) {
  181. field.setAccessible(true);
  182. if (null != field && null != javaBean) {
  183. Object value = field.get(javaBean);
  184. if (null != value) {
  185. Class<?> type = value.getClass();
  186. // 1.处理子属性,包括集合中的
  187. if (type.isArray()) {
  188. int len = Array.getLength(value);
  189. for (int i = 0; i < len; i++) {
  190. Object arrayObject = Array.get(value, i);
  191. SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(arrayObject.getClass()), arrayObject, referenceCounter);
  192. }
  193. } else if (value instanceof Collection<?>) {
  194. Collection<?> c = (Collection<?>) value;
  195. Iterator<?> it = c.iterator();
  196. while (it.hasNext()) {
  197. Object collectionObj = it.next();
  198. SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(collectionObj.getClass()), collectionObj, referenceCounter);
  199. }
  200. } else if (value instanceof Map<?, ?>) {
  201. Map<?, ?> m = (Map<?, ?>) value;
  202. Set<?> set = m.entrySet();
  203. for (Object o : set) {
  204. Entry<?, ?> entry = (Entry<?, ?>) o;
  205. Object mapVal = entry.getValue();
  206. SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(mapVal.getClass()), mapVal, referenceCounter);
  207. }
  208. } else if (!type.isPrimitive()
  209. && !StringUtils.startsWith(type.getPackage().getName(), "javax.")
  210. && !StringUtils.startsWith(type.getPackage().getName(), "java.")
  211. && !StringUtils.startsWith(field.getType().getName(), "javax.")
  212. && !StringUtils.startsWith(field.getName(), "java.")
  213. && referenceCounter.add(value.hashCode())) {
  214. SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(type), value, referenceCounter);
  215. }
  216. }
  217. // 2. 处理自身的属性
  218. SensitiveInfo annotation = field.getAnnotation(SensitiveInfo.class);
  219. if (field.getType().equals(String.class) && null != annotation) {
  220. String valueStr = (String) value;
  221. if (StringUtils.isNotBlank(valueStr)) {
  222. switch (annotation.type()) {
  223. case CHINESE_NAME: {
  224. field.set(javaBean, SensitiveInfoUtils.chineseName(valueStr));
  225. break;
  226. }
  227. case ID_CARD: {
  228. field.set(javaBean, SensitiveInfoUtils.idCardNum(valueStr));
  229. break;
  230. }
  231. case FIXED_PHONE: {
  232. field.set(javaBean, SensitiveInfoUtils.fixedPhone(valueStr));
  233. break;
  234. }
  235. case MOBILE_PHONE: {
  236. field.set(javaBean, SensitiveInfoUtils.mobilePhone(valueStr));
  237. break;
  238. }
  239. case ADDRESS: {
  240. field.set(javaBean, SensitiveInfoUtils.address(valueStr, 4));
  241. break;
  242. }
  243. case EMAIL: {
  244. field.set(javaBean, SensitiveInfoUtils.email(valueStr));
  245. break;
  246. }
  247. case BANK_CARD: {
  248. field.set(javaBean, SensitiveInfoUtils.bankCard(valueStr));
  249. break;
  250. }
  251. case CNAPS_CODE: {
  252. field.set(javaBean, SensitiveInfoUtils.cnapsCode(valueStr));
  253. break;
  254. }
  255. }
  256. }
  257. }
  258. }
  259. }
  260. }
  261. }
  262. //----------------------------------------------------------------------------------------------
  263. public static Method [] findAllMethod(Class<?> clazz){
  264. Method [] methods= clazz.getMethods();
  265. return methods;
  266. }
  267. //----------------------------------------------------------------------------------------------
  268. public static enum SensitiveType {
  269. /**
  270. * 中文名
  271. */
  272. CHINESE_NAME,
  273. /**
  274. * 身份证号
  275. */
  276. ID_CARD,
  277. /**
  278. * 座机号
  279. */
  280. FIXED_PHONE,
  281. /**
  282. * 手机号
  283. */
  284. MOBILE_PHONE,
  285. /**
  286. * 地址
  287. */
  288. ADDRESS,
  289. /**
  290. * 电子邮件
  291. */
  292. EMAIL,
  293. /**
  294. * 银行卡
  295. */
  296. BANK_CARD,
  297. /**
  298. * 公司开户银行联号
  299. */
  300. CNAPS_CODE;
  301. }
  302. }

声明注解:

  1. import java.lang.annotation.Documented;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Inherited;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. import com.ucf.platform.framework.core.util.SensitiveInfoUtils;
  8. /**
  9. * @Title: SensitiveInfo.java
  10. * @Copyright: Copyright (c) 2015
  11. * @Description: <br>
  12. * 敏感信息注解标记 <br>
  13. */
  14. @Target({ElementType.FIELD,ElementType.METHOD})
  15. @Retention(RetentionPolicy.RUNTIME)
  16. @Inherited
  17. @Documented
  18. public @interface SensitiveInfo {
  19. public SensitiveInfoUtils.SensitiveType type() ;
  20. }

测试:

  1. public class JavaBeanA {
  2. public JavaBeanA(String name,String id){
  3. }
  4. @SensitiveInfo(type=SensitiveType.CHINESE_NAME)
  5. private String name = "A先生";
  6. private JavaBeanB b;
  7. private Date date;
  8. private List<JavaBeanB> list;
  9. private Map<String,JavaBeanB> map;
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. public JavaBeanB getB() {
  17. return b;
  18. }
  19. public void setB(JavaBeanB b) {
  20. this.b = b;
  21. }
  22. public List<JavaBeanB> getList() {
  23. return list;
  24. }
  25. public void setList(List<JavaBeanB> list) {
  26. this.list = list;
  27. }
  28. public Map<String, JavaBeanB> getMap() {
  29. return map;
  30. }
  31. public void setMap(Map<String, JavaBeanB> map) {
  32. this.map = map;
  33. }
  34. public Date getDate() {
  35. return date;
  36. }
  37. public void setDate(Date date) {
  38. this.date = date;
  39. }
  40. }

  1. public class JavaBeanB {
  2. @SensitiveInfo(type=SensitiveType.CHINESE_NAME)
  3. private String name = "B先生";
  4. private JavaBeanA a;
  5. private Set<JavaBeanA> list;
  6. private Map<String,JavaBeanA> map;
  7. public String getName() {
  8. return name;
  9. }
  10. public void setName(String name) {
  11. this.name = name;
  12. }
  13. public JavaBeanA getA() {
  14. return a;
  15. }
  16. public void setA(JavaBeanA a) {
  17. this.a = a;
  18. }
  19. public Set<JavaBeanA> getList() {
  20. return list;
  21. }
  22. public void setList(Set<JavaBeanA> list) {
  23. this.list = list;
  24. }
  25. public Map<String, JavaBeanA> getMap() {
  26. return map;
  27. }
  28. public void setMap(Map<String, JavaBeanA> map) {
  29. this.map = map;
  30. }
  31. }

  1. public class SensitiveInfoUtilsTest {
  2. /**
  3. * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
  4. */
  5. @Test
  6. public void testChineseNameString() {
  7. System.out.println(SensitiveInfoUtils.chineseName("李先生"));
  8. }
  9. /**
  10. * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
  11. */
  12. @Test
  13. public void testChineseNameStringString() {
  14. System.out.println(SensitiveInfoUtils.chineseName("李","雷"));
  15. }
  16. /**
  17. * [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
  18. */
  19. @Test
  20. public void testIdCardNum() {
  21. System.out.println(SensitiveInfoUtils.idCardNum("1103541983073188711"));
  22. }
  23. /**
  24. * [固定电话] 后四位,其他隐藏<例子:****1234>
  25. */
  26. @Test
  27. public void testFixedPhone() {
  28. System.out.println(SensitiveInfoUtils.fixedPhone("01077482277"));
  29. }
  30. /**
  31. * [手机号码] 前三位,后四位,其他隐藏<例子:138******1234>
  32. */
  33. @Test
  34. public void testMobilePhone() {
  35. System.out.println(SensitiveInfoUtils.mobilePhone("13777446578"));
  36. }
  37. /**
  38. * [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****>
  39. */
  40. @Test
  41. public void testAddress() {
  42. System.out.println(SensitiveInfoUtils.address("北京朝阳区酒仙桥中路26号院4号楼人人大厦",8));
  43. }
  44. /**
  45. * [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com>
  46. */
  47. @Test
  48. public void testEmail() {
  49. System.out.println(SensitiveInfoUtils.email("66374777@qq.com"));
  50. }
  51. /**
  52. * [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234>
  53. */
  54. @Test
  55. public void testBankCard() {
  56. System.out.println(SensitiveInfoUtils.bankCard("6228480402565890018"));
  57. }
  58. /**
  59. * [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号<例子:12********>
  60. */
  61. @Test
  62. public void testCnapsCode() {
  63. System.out.println(SensitiveInfoUtils.cnapsCode("102100029679"));
  64. }
  65. /**
  66. * 获取脱敏json串 <注意:递归引用会导致java.lang.StackOverflowError>
  67. */
  68. @Test
  69. public void testGetJson() {
  70. // ThreadPoolExecutor consumeExecutor = new ThreadPoolExecutor(30, 30 + 10, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(30 + 10), new ThreadFactory() {
  71. // @Override
  72. // public Thread newThread(Runnable r) {
  73. // Thread myThread = new Thread(r);
  74. // myThread.setName("TT");
  75. // return myThread;
  76. // }
  77. // }, new ThreadPoolExecutor.CallerRunsPolicy());
  78. // while (true) {
  79. // consumeExecutor.execute(new Runnable() {
  80. // @Override
  81. // public void run() {}
  82. // });
  83. // }
  84. JavaBeanA a1 = new JavaBeanA("","");
  85. JavaBeanA a2 = new JavaBeanA("","");
  86. JavaBeanB b1 = new JavaBeanB();
  87. a1.setB(b1);
  88. // a1.setDate(new Date());
  89. List<JavaBeanB> a1l = new ArrayList<JavaBeanB>();
  90. a1l.add(b1);
  91. a1.setList(a1l);
  92. Map<String, JavaBeanB> a1m = new HashMap<String, JavaBeanB>();
  93. a1m.put("b1", b1);
  94. a1.setMap(a1m);
  95. b1.setA(a2);
  96. Set<JavaBeanA> b1l = new HashSet<JavaBeanA>();
  97. b1.setList(b1l);
  98. Map<String, JavaBeanA> b1m = new HashMap<String, JavaBeanA>();
  99. b1m.put("a2", a2);
  100. b1.setMap(b1m);
  101. long t = System.currentTimeMillis();
  102. System.out.println(t);
  103. System.out.println(SensitiveInfoUtils.getJson(a1));
  104. System.out.println(System.currentTimeMillis()-t);
  105. System.out.println(JSON.toJSON(a1));
  106. System.out.println(System.currentTimeMillis()-t);
  107. }
  108. }

测试结果:

  1. 李**
  2. 李*
  3. ***************8711
  4. *******2277
  5. 137****6578
  6. 北京朝阳区酒仙桥中路26号********
  7. 6*******@qq.com
  8. 622848*********0018
  9. 10**********
  10. 1443435915750
  11. {"b":{"a":{"b":null,"date":null,"list":[],"map":null,"name":"A**"},"list":[],"map":{"a2":{"b":null,"date":null,"list":[],"map":null,"name":"A**"}},"name":"B**"},"date":null,"list":[{"a":{"b":null,"date":null,"list":[],"map":null,"name":"A**"},"list":[],"map":{"a2":{"b":null,"date":null,"list":[],"map":null,"name":"A**"}},"name":"B**"}],"map":{"b1":{"a":{"b":null,"date":null,"list":[],"map":null,"name":"A**"},"list":[],"map":{"a2":{"b":null,"date":null,"list":[],"map":null,"name":"A**"}},"name":"B**"}},"name":"A**"}
  12. 289
  13. {"b":{"a":{"name":"A先生"},"list":[],"map":{"a2":{"name":"A先生"}},"name":"B先生"},"list":[{"a":{"name":"A先生"},"list":[],"map":{"a2":{"name":"A先生"}},"name":"B先生"}],"map":{"b1":{"a":{"name":"A先生"},"list":[],"map":{"a2":{"name":"A先生"}},"name":"B先生"}},"name":"A先生"}
  14. 300



使用了google 的API, 可以使用maven在添加,配置如下:

  1. <!-- gson -->
  2. <dependency>
  3. <groupId>com.google.code.gson</groupId>
  4. <artifactId>gson</artifactId>
  5. </dependency>

.


说明:在需要脱敏的字段上使用定义好的注解,在具体的使用时用SensitiveInfoUtils.getJson(a1),如果不需要脱敏的输出,尽量不要打印JSON,使用对象的toString()输出。效率更高。


来源:https://blog.csdn.net/liuc0317/article/details/48787793
posted @ 2022-09-25 23:02  程序员小明1024  阅读(58)  评论(0编辑  收藏  举报