<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
目前正在開發一個SpringBoot專案,此專案有Web端和微信小程式端。web端提供給工作人員使用,微信小程式提供給群眾進行預約操作。專案中有部分敏感資料需要脫敏傳遞給微信小程式,給與群眾檢視。
專案中,由於使用端有兩個,對於兩個端的資料許可權並不一樣。Web端可以檢視所有資料,小程式端只能檢視脫敏後的資料。
需要開發一個通用脫敏功能
使用註解方式
,來支援對指定欄位,不同欄位,多種脫敏操作,並可以脫離物件。
使用工具物件,通過泛型傳參,來支援對不同物件的脫敏操作。
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定義資料脫敏 * * 例如: 身份證,手機號等資訊進行模糊處理 * * @author lzddddd */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Sensitive { /** * 脫敏資料型別 */ SensitiveType type() default SensitiveType.CUSTOMER; /** * 前置不需要打碼的長度 */ int prefixNoMaskLen() default 0; /** * 後置不需要打碼的長度 */ int suffixNoMaskLen() default 0; /** * 用什麼打碼 */ String symbol() default "*"; }
public enum SensitiveType { /** * 自定義 */ CUSTOMER, /** * 名稱 **/ CHINESE_NAME, /** * 身份證證件號 **/ ID_CARD_NUM, /** * 手機號 **/ MOBILE_PHONE, /** * 固定電話 */ FIXED_PHONE, /** * 密碼 **/ PASSWORD, /** * 銀行卡號 */ BANKCARD, /** * 郵箱 */ EMAIL, /** * 地址 */ ADDRESS, }
import com.ruoyi.common.annotation.Sensitive; import com.ruoyi.common.constant.HttpStatus; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.enums.SensitiveType; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Field; import java.util.*; @Slf4j public class DesensitizedUtils<T> { /** * 脫敏資料列表 */ private List<T> list; /** * 註解列表 */ private List<Object[]> fields; /** * 實體物件 */ public Class<T> clazz; public DesensitizedUtils(Class<T> clazz) { this.clazz = clazz; } /** * 初始化資料 * * @param list 需要處理資料 */ public void init(List<T> list){ if (list == null) { list = new ArrayList<T>(); } this.list = list; // 得到所有定義欄位 createSensitiveField(); } /** * 初始化資料 * * @param t 需要處理資料 */ public void init(T t){ list = new ArrayList<T>(); if (t != null) { list.add(t); } // 得到所有定義欄位 createSensitiveField(); } /** * 得到所有定義欄位 */ private void createSensitiveField() { this.fields = new ArrayList<Object[]>(); List<Field> tempFields = new ArrayList<>(); tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); for (Field field : tempFields) { // 單註解 if (field.isAnnotationPresent(Sensitive.class)) { putToField(field, field.getAnnotation(Sensitive.class)); } // 多註解 // if (field.isAnnotationPresent(Excels.class)) // { // Excels attrs = field.getAnnotation(Excels.class); // Excel[] excels = attrs.value(); // for (Excel excel : excels) // { // putToField(field, excel); // } // } } } /** * 對list資料來源將其裡面的資料進行脫敏處理 * * @param list * @return 結果 */ public AjaxResult desensitizedList(List<T> list){ if (list == null){ return AjaxResult.error("脫敏資料為空"); } // 初始化資料 this.init(list); int failTimes = 0; for (T t: this.list) { if ((Integer)desensitization(t).get("code") != HttpStatus.SUCCESS){ failTimes++; } } if (failTimes >0){ return AjaxResult.error("脫敏操作中出現失敗",failTimes); } return AjaxResult.success(); } /** * 放到欄位集合中 */ private void putToField(Field field, Sensitive attr) { if (attr != null) { this.fields.add(new Object[] { field, attr }); } } /** * 脫敏:JavaBean模式脫敏 * * @param t 需要脫敏的物件 * @return */ public AjaxResult desensitization(T t) { if (t == null){ return AjaxResult.error("脫敏資料為空"); } // 初始化資料 init(t); try { // 遍歷處理需要進行 脫敏的欄位 for (Object[] os : fields) { Field field = (Field) os[0]; Sensitive sensitive = (Sensitive) os[1]; // 設定實體類私有屬性可存取 field.setAccessible(true); desensitizeField(sensitive,t,field); } return AjaxResult.success(t); } catch (Exception e) { e.printStackTrace(); log.error("紀錄檔脫敏處理失敗,回滾,詳細資訊:[{}]", e); return AjaxResult.error("脫敏處理失敗",e); } } /** * 對類的屬性進行脫敏 * * @param attr 脫敏引數 * @param vo 脫敏物件 * @param field 脫敏屬性 * @return */ private void desensitizeField(Sensitive attr, T vo, Field field) throws IllegalAccessException { if (attr == null || vo == null || field == null){ return ; } // 讀取物件中的屬性 Object value = field.get(vo); SensitiveType sensitiveType = attr.type(); int prefixNoMaskLen = attr.prefixNoMaskLen(); int suffixNoMaskLen = attr.suffixNoMaskLen(); String symbol = attr.symbol(); //獲取屬性後現在預設處理的是String型別,其他型別資料可延伸 Object val = convertByType(sensitiveType, value, prefixNoMaskLen, suffixNoMaskLen, symbol); field.set(vo, val); } /** * 以類的屬性的get方法方法形式獲取值 * * @param o 物件 * @param name 屬性名 * @return value * @throws Exception */ private Object getValue(Object o, String name) throws Exception { if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) { Class<?> clazz = o.getClass(); Field field = clazz.getDeclaredField(name); field.setAccessible(true); o = field.get(o); } return o; } /** * 根據不同註解型別處理不同欄位 */ private Object convertByType(SensitiveType sensitiveType, Object value, int prefixNoMaskLen, int suffixNoMaskLen, String symbol) { switch (sensitiveType) { case CUSTOMER: value = customer(value, prefixNoMaskLen, suffixNoMaskLen, symbol); break; case CHINESE_NAME: value = chineseName(value, symbol); break; case ID_CARD_NUM: value = idCardNum(value, symbol); break; case MOBILE_PHONE: value = mobilePhone(value, symbol); break; case FIXED_PHONE: value = fixedPhone(value, symbol); break; case PASSWORD: value = password(value, symbol); break; case BANKCARD: value = bankCard(value, symbol); break; case EMAIL: value = email(value, symbol); break; case ADDRESS: value = address(value, symbol); break; } return value; } /*--------------------------下面的脫敏工具類也可以單獨對某一個欄位進行使用-------------------------*/ /** * 【自定義】 根據設定進行設定 * * @param value 需處理資料 * @param symbol 填充字元 * @return 脫敏後資料 */ public Object customer(Object value, int prefixNoMaskLen, int suffixNoMaskLen, String symbol) { //針對字串的處理 if (value instanceof String){ return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol); } return value; } /** * 對字串進行脫敏處理 * * @param s 需處理資料 * @param prefixNoMaskLen 開頭展示字元長度 * @param suffixNoMaskLen 結尾展示字元長度 * @param symbol 填充字元 * @return */ private String handleString(String s, int prefixNoMaskLen, int suffixNoMaskLen, String symbol){ // 是否為空 if (StringUtils.isBlank(s)) { return ""; } // 如果設定為空之類使用 * 代替 if (StringUtils.isBlank(symbol)){ symbol = "*"; } // 對長度進行判斷 int length = s.length(); if (length > prefixNoMaskLen + suffixNoMaskLen){ String namePrefix = StringUtils.left(s, prefixNoMaskLen); String nameSuffix = StringUtils.right(s, suffixNoMaskLen); s = StringUtils.rightPad(namePrefix, StringUtils.length(s) - suffixNoMaskLen, symbol).concat(nameSuffix); } return s; } /** * 【中文姓名】只顯示第一個漢字,其他隱藏為2個星號,比如:李** * * @param value 需處理資料 * @param symbol 填充字元 * @return 脫敏後資料 */ public String chineseName(Object value, String symbol) { //針對字串的處理 if (value instanceof String){ // 對前後長度進行設定 預設 開頭只展示一個字元 int prefixNoMaskLen = 1; int suffixNoMaskLen = 0; return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol); } return ""; } /** * 【身份證號】顯示最後四位,其他隱藏。共計18位元或者15位,比如:*************1234 * * @param value 需處理資料 * @param symbol 填充字元 * @return 脫敏後資料 */ public String idCardNum(Object value, String symbol) { //針對字串的處理 if (value instanceof String){ // 對前後長度進行設定 預設 結尾只展示四個字元 int prefixNoMaskLen = 0; int suffixNoMaskLen = 4; return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol); } return ""; } /** * 【固定電話】 顯示後四位,其他隱藏,比如:*******3241 * * @param value 需處理資料 * @param symbol 填充字元 * @return 脫敏後資料 */ public String fixedPhone(Object value, String symbol) { //針對字串的處理 if (value instanceof String){ // 對前後長度進行設定 預設 結尾只展示四個字元 int prefixNoMaskLen = 0; int suffixNoMaskLen = 4; return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol); } return ""; } /** * 【手機號碼】前三位,後四位,其他隱藏,比如:135****6810 * * @param value 需處理資料 * @param symbol 填充字元 * @return 脫敏後資料 */ public String mobilePhone(Object value, String symbol) { //針對字串的處理 if (value instanceof String){ // 對前後長度進行設定 預設 開頭只展示三個字元 結尾只展示四個字元 int prefixNoMaskLen = 3; int suffixNoMaskLen = 4; return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol); } return ""; } /** * 【地址】只顯示到地區,不顯示詳細地址,比如:湖南省長沙市嶽麓區*** * 只能處理 省市區的資料 * * @param value 需處理資料 * @param symbol 填充字元 * @return */ public String address(Object value, String symbol) { //針對字串的處理 if (value instanceof String){ // 對前後長度進行設定 預設 開頭只展示九個字元 int prefixNoMaskLen = 9; int suffixNoMaskLen = 0; return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol); } return ""; } /** * 【電子郵箱】 郵箱字首僅顯示第一個字母,字首其他隱藏,用星號代替,@及後面的地址顯示,比如:d**@126.com * * @param value 需處理資料 * @param symbol 填充字元 * @return 脫敏後資料 */ public String email(Object value, String symbol) { //針對字串的處理 if (value instanceof String){ // 對前後長度進行設定 預設 開頭只展示一個字元 結尾只展示@及後面的地址 int prefixNoMaskLen = 1; int suffixNoMaskLen = 4; String s = (String) value; if (StringUtils.isBlank(s)) { return ""; } // 獲取最後一個@ int lastIndex = StringUtils.lastIndexOf(s, "@"); if (lastIndex <= 1) { return s; } else { suffixNoMaskLen = s.length() - lastIndex; } return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol); } return ""; } /** * 【銀行卡號】前六位,後四位,其他用星號隱藏每位1個星號,比如:6222600**********1234 * * @param value 需處理資料 * @param symbol 填充字元 * @return 脫敏後資料 */ public String bankCard(Object value, String symbol) { //針對字串的處理 if (value instanceof String){ // 對前後長度進行設定 預設 開頭只展示六個字元 結尾只展示四個字元 int prefixNoMaskLen = 6; int suffixNoMaskLen = 4; return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol); } return ""; } /** * 【密碼】密碼的全部字元都用*代替,比如:****** * * @param value 需處理資料 * @param symbol 填充字元 * @return */ public String password(Object value,String symbol) { //針對字串的處理 if (value instanceof String){ // 對前後長度進行設定 預設 開頭只展示六個字元 結尾只展示四個字元 int prefixNoMaskLen = 0; int suffixNoMaskLen = 0; return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol); } return ""; } }
public class User { private static final long serialVersionUID = 1L; /** 普通使用者ID */ private Long userId; /** 暱稱 */ @Sensitive(type = SensitiveType.CUSTOMER,prefixNoMaskLen = 2,suffixNoMaskLen = 1) private String nickName; /** 姓名 */ @Sensitive(type = SensitiveType.CHINESE_NAME) private String userName; /** 身份證 */ @Sensitive(type = SensitiveType.ID_CARD_NUM) private String identityCard; /** 手機號碼 */ @Sensitive(type = SensitiveType.MOBILE_PHONE) private String phoneNumber; }
// 脫敏物件 User user = new User(); ...... DesensitizedUtils<User> desensitizedUtils = new DesensitizedUtils<>(User.class); desensitizedUtils.desensitization(user); //脫敏佇列 List<User> users = new ArrayList<>(); ...... DesensitizedUtils<User> desensitizedUtils = new DesensitizedUtils<>(User.class); desensitizedUtils.desensitizedList(users);
到此這篇關於SpringBoot專案中新增脫敏功能的文章就介紹到這了,更多相關SpringBoot脫敏內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45