這篇文章主要講解了“Java如何用poi完成Excel導(dǎo)出數(shù)據(jù)脫敏”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Java如何用poi完成Excel導(dǎo)出數(shù)據(jù)脫敏”吧!
網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、小程序定制開(kāi)發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了貢覺(jué)免費(fèi)建站歡迎大家使用!
脫敏的百度百科的定義:是指對(duì)某些敏感信息通過(guò)脫敏規(guī)則進(jìn)行數(shù)據(jù)的變形,實(shí)現(xiàn)敏感隱私數(shù)據(jù)的可靠保護(hù)。在涉及客戶安全數(shù)據(jù)或者一些商業(yè)性敏感數(shù)據(jù)的情況下,在不違反系統(tǒng)規(guī)則條件下,對(duì)真實(shí)數(shù)據(jù)進(jìn)行改造并提供測(cè)試使用,如身份證號(hào)、手機(jī)號(hào)、卡號(hào)、客戶號(hào)等個(gè)人信息都需要進(jìn)行數(shù)據(jù)脫敏。
這塊如果嚴(yán)格按照定義,實(shí)現(xiàn)身份證等數(shù)據(jù)的變形,筆者還沒(méi)實(shí)現(xiàn),因?yàn)檫@個(gè)設(shè)計(jì)脫敏規(guī)則對(duì)應(yīng),需要用戶指定規(guī)則,我們內(nèi)部的脫敏系統(tǒng)還是有些邏輯在的,這里就不細(xì)說(shuō)了,這次主要實(shí)現(xiàn)的是加*號(hào)【后期再加自定義吧,畢竟常見(jiàn)的都是星號(hào)】,方便導(dǎo)出數(shù)據(jù)的時(shí)候隱藏非必須字段。
維護(hù)數(shù)據(jù)
實(shí)現(xiàn)的功能主要分為三種:
1. 隱去收尾,適合固定長(zhǎng)度,比如:手機(jī)號(hào),身份證
2. 隱去部分,不固定長(zhǎng)度,比如:姓名,地址
3. 隱去特定部分,特別表示保留,比如:郵箱
簡(jiǎn)單地實(shí)現(xiàn)的效果為
13112345678 --> 131****1234
張三 -->張*
羅納爾迪尼奧 --> 羅納***奧
wuyun@163.com -> w****@16com.com
脫敏效果
注解還是通過(guò)@Excel 的屬性來(lái)實(shí)現(xiàn)的 ,在@Excel 這個(gè)地方加入了desensitizationRule屬性,可以在desensitizationRule屬性配置對(duì)應(yīng)的格式遍可以得到對(duì)應(yīng)的結(jié)果
/** * 數(shù)據(jù)脫敏規(guī)則 * 規(guī)則1: 采用保留頭和尾的方式,中間數(shù)據(jù)加星號(hào) * 如: 身份證 6_4 則保留 370101********1234 * 手機(jī)號(hào) 3_4 則保留 131****1234 * 規(guī)則2: 采用確定隱藏字段的進(jìn)行隱藏,優(yōu)先保留頭 * 如: 姓名 1,3 表示最大隱藏3位,最小一位 * 李 --> * * 李三 --> 李* * 張全蛋 --> 張*蛋 * 李張全蛋 --> 李**蛋 * 尼古拉斯.李張全蛋 -> 尼古拉***張全蛋 * 規(guī)則3: 特殊符號(hào)后保留 * 如: 郵箱 1~@ 表示只保留第一位和@之后的字段 * afterturn@wupaas.com -> a********@wupaas.com * 復(fù)雜版本請(qǐng)使用接口 * {@link cn.afterturn.easypoi.handler.inter.IExcelDataHandler} */ public String desensitizationRule() default "";
可以根據(jù)注釋看到,3個(gè)簡(jiǎn)單規(guī)則的使用方法,還是比較清晰的,主要是:
用下劃線分隔的保留頭尾的第一種格式
該格式首和尾都可以是0,表示不保留
用逗號(hào)分隔的隱藏部分?jǐn)?shù)據(jù)的第二種格式
保留規(guī)則以對(duì)稱為主
用~號(hào)來(lái)區(qū)分的,保留特殊字符后的格式的
如果存在多個(gè)特殊字符,只保留最后一位的數(shù)據(jù)
具體實(shí)現(xiàn)代碼如下:
/** * 特定字符分隔,添加星號(hào) * @param start * @param mark * @param value * @return */ private static String markSpilt(int start, String mark, String value) { if (value == null) { return null; } int end = value.lastIndexOf(mark); if (end <= start) { return value; } return StringUtils.left(value, start).concat(StringUtils.leftPad(StringUtils.right(value, value.length() - end), value.length() - start, "*")); } /** * 部分?jǐn)?shù)據(jù)截取,優(yōu)先對(duì)稱截取 * @param start * @param end * @param value * @return */ private static String subMaxString(int start, int end, String value) { if (value == null) { return null; } if (start > end) { throw new IllegalArgumentException("start must less end"); } int len = value.length(); if (len <= start) { return StringUtils.leftPad("", len, "*"); } else if (len > start && len <= end) { if (len == 1) { return value; } if (len == 2) { return StringUtils.left(value, 1).concat("*"); } return StringUtils.left(value, 1).concat(StringUtils.leftPad(StringUtils.right(value, 1), StringUtils.length(value) - 1, "*")); } else { start = (int) Math.ceil((len - end + 0.0D) / 2); end = len - start - end; end = end == 0 ? 1 : end; return StringUtils.left(value, start).concat(StringUtils.leftPad(StringUtils.right(value, end), len - start, "*")); } } /** * 收尾截取數(shù)據(jù) * @param start * @param end * @param value * @return */ private static String subStartEndString(int start, int end, String value) { if (value == null) { return null; } if (value.length() <= start + end) { return value; } return StringUtils.left(value, start).concat(StringUtils.leftPad(StringUtils.right(value, end), StringUtils.length(value) - start, "*")); }
使用起來(lái)也比較簡(jiǎn)單,通過(guò)簡(jiǎn)單的注解配置,就可以實(shí)現(xiàn)
@Excel(name = "姓名", desensitizationRule = "1,6") private String name; @Excel(name = "身份證", desensitizationRule = "6_4") private String card; @Excel(name = "手機(jī)號(hào)", desensitizationRule = "3_4") private String phone; @Excel(name = "郵箱", desensitizationRule = "3~@") private String email; // ---------------下面是生成excel的代碼------- // 都是測(cè)試數(shù)據(jù),就簡(jiǎn)單生成了部分 Listlist = new ArrayList<>(); for (int i = 0; i < 20; i++) { DesensitizationEntity entity = new DesensitizationEntity(); entity.setCard("37010119900101123" + i % 10); entity.setName("張三"); entity.setPhone("1311234567" + i % 10); entity.setEmail(i % 10 + "ttttt@afterturn.com"); list.add(entity); } Date start = new Date(); ExportParams params = new ExportParams("脫敏測(cè)試", "脫敏測(cè)試", ExcelType.XSSF); Workbook workbook = ExcelExportUtil.exportExcel(params, DesensitizationEntity.class, list); System.out.println(new Date().getTime() - start.getTime()); File savefile = new File("D:/home/excel/"); if (!savefile.exists()) { savefile.mkdirs(); } FileOutputStream fos = new FileOutputStream("D:/home/excel/ExcelDesensitizationTest.xlsx"); workbook.write(fos); fos.close();
生成的效果如下,可以看到幾個(gè)場(chǎng)景都有所覆蓋,基本上常用的字段也就
PS: 因?yàn)樽兎N注解和注解可以通用,所以大家在使用變種注解的時(shí)候也可以使用這種方式來(lái)脫敏數(shù)據(jù)。
這里就不舉例子了,只需要new ExcelExportEntity的之后調(diào)用setDesensitizationRule(rule),把對(duì)應(yīng)的規(guī)則設(shè)置就好,規(guī)則和上面保持一致。
模板脫敏
目前從群里反應(yīng)和問(wèn)題數(shù)量看,模板已經(jīng)是最常用的方式了,雖然我一致推薦注解,但明顯模板比注解簡(jiǎn)單的,對(duì)代碼的侵入一些,我自己寫(xiě)的特殊標(biāo)簽,也日益豐富了。
脫敏的標(biāo)簽取自:desensitizationRule 的兩個(gè)單詞的頭兩個(gè)字符 deru,用來(lái)表示這個(gè)數(shù)據(jù)需要脫敏。使用方法也比較簡(jiǎn)單deru:1_3,即deru:后面跟具體的規(guī)則,規(guī)則使用方式和上面介紹的 保持一致,這里就不重復(fù)贅述了,下面給大家看下例子:
/** * 這個(gè)包含兩個(gè)規(guī)則 * 1. deru: 代表這個(gè)數(shù)據(jù)需要脫敏 * 2. dict: 代表這個(gè)需要調(diào)用字典接口轉(zhuǎn)移 **/ deru:1_1;dict:mtype;t.supMaterialList.mtype
脫敏在標(biāo)簽處理級(jí)別中屬于最低級(jí),所有標(biāo)簽處理后,才會(huì)處理這個(gè)標(biāo)簽,和他的書(shū)寫(xiě)順序無(wú)關(guān),比如上面的例子他寫(xiě)著第一個(gè),也是先處理了字典之后才處理標(biāo)簽。
核心代碼如下:
/** * 根據(jù)模板解析函數(shù)獲取值 * @param funStr * @param map * @return */ private Object getValByHandler(String funStr,Mapmap, Cell cell) throws Exception { // step 2. 判斷是否含有解析函數(shù) if (isHasSymbol(funStr, NUMBER_SYMBOL)) { funStr = funStr.replaceFirst(NUMBER_SYMBOL, ""); } boolean isStyleBySelf = false; if (isHasSymbol(funStr, STYLE_SELF)) { isStyleBySelf = true; funStr = funStr.replaceFirst(STYLE_SELF, ""); } boolean isDict = false; String dict = null; if (isHasSymbol(funStr, DICT_HANDLER)) { isDict = true; dict = funStr.substring(funStr.indexOf(DICT_HANDLER) + 5).split(";")[0]; funStr = funStr.replaceFirst(DICT_HANDLER, ""); funStr = funStr.replaceFirst(dict + ";", ""); } boolean isI18n = false; if (isHasSymbol(funStr, I18N_HANDLER)) { isI18n = true; funStr = funStr.replaceFirst(I18N_HANDLER, ""); } //這里判斷是否包含注解規(guī)則,如果有注解規(guī)則就把注解規(guī)則挑出來(lái),然后刪除掉規(guī)則 boolean isDern = false; String dern = null; if (isHasSymbol(funStr, DESENSITIZATION_RULE)) { isDern = true; dern = funStr.substring(funStr.indexOf(DESENSITIZATION_RULE) + 5).split(";")[0]; funStr = funStr.replaceFirst(DESENSITIZATION_RULE, ""); funStr = funStr.replaceFirst(dern + ";", ""); } if (isHasSymbol(funStr, MERGE)) { String mergeStr = PoiPublicUtil.getElStr(funStr,MERGE); funStr = funStr.replace(mergeStr, ""); mergeStr = mergeStr.replaceFirst(MERGE, ""); try { int colSpan = (int)Double.parseDouble(PoiPublicUtil.getRealValue(mergeStr, map).toString()); PoiMergeCellUtil.addMergedRegion(cell.getSheet(), cell.getRowIndex(), cell.getRowIndex() , cell.getColumnIndex(), cell.getColumnIndex() + colSpan - 1); } catch (Exception e) { LOGGER.error(e.getMessage(),e); } } Object obj = funStr.indexOf(START_STR) == -1 ? eval(funStr, map) : PoiPublicUtil.getRealValue(funStr, map); if (isDict) { obj = dictHandler.toName(dict, null, funStr, obj); } if (isI18n) { obj = i18nHandler.getLocaleName(obj.toString()); } // 這里就是調(diào)用脫敏規(guī)則,和注解是一個(gè)工具類,所以規(guī)則一致 if (isDern) { obj = PoiDataDesensitizationUtil.desensitization(dern,obj); } return obj; }
給大家看下模板跑出來(lái)的效果:
最后結(jié)果
感謝各位的閱讀,以上就是“Java如何用poi完成Excel導(dǎo)出數(shù)據(jù)脫敏”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Java如何用poi完成Excel導(dǎo)出數(shù)據(jù)脫敏這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!