一、前言
关于Java在实现涉及用户/客户敏感数据(如手机号、姓名等)打印输出的时候,通常需要将其转换为掩码”*“处理,下面通过具体的源码示例进行说明。
二、示例说明
下面示例依赖fastjson包
package test;@b@@b@import java.util.regex.Matcher;@b@import java.util.regex.Pattern;@b@import org.apache.commons.lang.StringUtils;@b@import com.alibaba.fastjson.JSON;@b@import com.alibaba.fastjson.JSONObject;@b@@b@public class CustomerSecretDataFilter {@b@ @b@ private static Pattern pattern = Pattern.compile("[0-9a-zA-Z]");@b@ //手机号掩码字段@b@ private static String CUST_PHONE="customerPhone,mobile,MOBILE";@b@ //客户姓名掩码字段@b@ private static String CUST_NAME="customerName,custName,CUST_NAME,clientName";@b@ @b@ /**@b@ * 客户手机号,处理日志字符串,返回脱敏后的字符串@b@ * @param msg@b@ * @return@b@ */@b@ private static String maskCustPhoneMsg(final String message){ @b@ String msg = handleMsg(CUST_PHONE,message);@b@ return msg;@b@ }@b@ /**@b@ * 客户姓名,处理日志字符串,返回脱敏后的字符串@b@ * @param msg@b@ * @return@b@ */@b@ private static String maskCustNameMsg(final String message){ @b@ String msg = handleMsg(CUST_NAME,message);@b@ return msg;@b@ }@b@ @b@ @b@ /**@b@ * 处理日志字符串,返回脱敏后的字符串@b@ * @param msg@b@ * @return@b@ */@b@ private static String handleMsg(final String handleAttr,final String fullText){@b@ @b@ String msg = new String(fullText);@b@ @b@ //处理字符串@b@ if(handleAttr!=null && handleAttr.length()>0){@b@ @b@ String[] keyArr = handleAttr.split(",");@b@ for(String key: keyArr){@b@ // 找key@b@ int index= -1; @b@ do{@b@ index = msg.indexOf(key, index+1);@b@ if(index!=-1){ @b@ // 判断key是否为单词字符@b@ if(isWordChar(msg,key,index)){@b@ continue;@b@ }@b@ // 确定是单词无疑....................................@b@ // 寻找值的开始位置.................................@b@ int valueStart = getValueStartIndex(msg,index + key.length());@b@ @b@ //查找值的结束位置(逗号,分号)........................@b@ int valueEnd = getValuEndEIndex(msg,valueStart);@b@ @b@ // 对获取的值进行脱敏 @b@ String subStr = msg.substring(valueStart, valueEnd);@b@ if(CUST_PHONE.equals(handleAttr)){@b@ subStr = tuominPhone(subStr);@b@ }@b@ if(CUST_NAME.equals(handleAttr)){@b@ subStr = replaceMask(subStr, 1, 0, "*");@b@ }@b@ @b@ ///////////////////////////@b@ msg = msg.substring(0,valueStart) + subStr + msg.substring(valueEnd);@b@ }@b@ }while(index!=-1);@b@ @b@ }@b@ }@b@ @b@ @b@ return msg;@b@ }@b@ @b@ @b@ /**@b@ * 判断从字符串msg获取的key值是否为单词 , index为key在msg中的索引值@b@ * @return@b@ */@b@ private static boolean isWordChar(String msg,String key, int index){@b@ @b@ // 必须确定key是一个单词............................@b@ if(index!=0){ //判断key前面一个字符@b@ char preCh = msg.charAt(index-1);@b@ Matcher match = pattern.matcher(preCh+"");@b@ if(match.matches()){@b@ return true;@b@ }@b@ }@b@ //判断key后面一个字符@b@ char nextCh = msg.charAt(index+key.length());@b@ Matcher match = pattern.matcher(nextCh+"");@b@ if(match.matches()){@b@ return true;@b@ }@b@ @b@ return false;@b@ @b@ }@b@ @b@ /**@b@ * 获取value值的开始位置@b@ * @param msg 要查找的字符串@b@ * @param valueStart 查找的开始位置@b@ * @return@b@ */@b@ private static int getValueStartIndex(String msg, int valueStart ){@b@ // 寻找值的开始位置.................................@b@ do{@b@ char ch = msg.charAt(valueStart);@b@ if(ch == ':' || ch == '='){ // key 与 value的分隔符@b@ valueStart ++;@b@ ch = msg.charAt(valueStart);@b@ if(ch == '"'){@b@ valueStart ++;@b@ }@b@ break; //找到值的开始位置@b@ }else{@b@ valueStart ++;@b@ }@b@ @b@ }while(true);@b@ return valueStart;@b@ }@b@ @b@ /**@b@ * 获取value值的结束位置@b@ * @return@b@ */@b@ private static int getValuEndEIndex(String msg,int valueEnd){@b@ @b@ do{@b@ if(valueEnd == msg.length()){@b@ break;@b@ }@b@ char ch = msg.charAt(valueEnd);@b@ @b@ if(ch == '"'){ // 引号时,判断下一个值是结束,分号还是逗号决定是否为值的结束@b@ if(valueEnd+1 == msg.length()){@b@ break;@b@ }@b@ char nextCh = msg.charAt(valueEnd+1);@b@ if(nextCh ==';' || nextCh == ',' || nextCh == '}' || nextCh == ']'){@b@ // 去掉前面的 \ 处理这种形式的数据 "account_num\\\":\\\"6230958600001008\\\"@b@ while(valueEnd>0 ){@b@ char preCh = msg.charAt(valueEnd-1);@b@ if(preCh != '\\'){@b@ break;@b@ }@b@ valueEnd--;@b@ }@b@ break;@b@ }else{@b@ valueEnd ++;@b@ }@b@ }else if (ch ==';' || ch == ','){@b@ break;@b@ }else{@b@ valueEnd ++;@b@ }@b@ @b@ }while(true);@b@ @b@ return valueEnd;@b@ }@b@ @b@ /**@b@ * 公用方法,以后可以使用这个方法进行掩码@b@ * @param str@b@ * @param start@b@ * @param end@b@ * @param placeStr@b@ * @return@b@ */@b@ private static String replaceMask(String str, int start, int end, String placeStr){@b@ if(StringUtils.isBlank(str)){@b@ return "";@b@ }@b@ if(start < 0 || end < 0){@b@ return str;@b@ }@b@ if(StringUtils.isBlank(placeStr)){@b@ placeStr = "*";@b@ }@b@ return str.replaceAll("(?<=[\\S]{"+ start +"})\\S(?=[\\S]{"+ end +"})", placeStr);@b@ }@b@ @b@ private static String tuominPhone(String submsg){@b@@b@ StringBuffer sbResult = new StringBuffer();@b@ if(submsg!=null && submsg.length()>0){@b@ int len = submsg.length();@b@ if(len >= 4){ //4位及以上的 隐掉中间4位@b@ for(int i = len-1;i>=0;i--){@b@ if(len-i<5 || len-i>8){@b@ sbResult.insert(0, submsg.charAt(i));@b@ }else{@b@ sbResult.insert(0, '*');@b@ }@b@ }@b@ }else{ //4位以下的全部使用 *@b@ for(int i =0;i<len;i++){@b@ sbResult.append('*');@b@ }@b@ }@b@ }@b@ return sbResult.toString();@b@ }@b@ @b@ @b@ @b@ public static String maskSecretLog(final Object obj){@b@ //对象转json串@b@ String result = JSON.toJSONString(obj);@b@ try{@b@ result=maskCustPhoneMsg(result);@b@ result=maskCustNameMsg(result);@b@ }catch(Exception e){@b@ e.printStackTrace();@b@ }@b@ return result;@b@ }@b@ public static String maskSecretLog(final String str){@b@ //对象赋值@b@ String result = str;@b@ try{@b@ result=maskCustPhoneMsg(result);@b@ result=maskCustNameMsg(result);@b@ }catch(Exception e){@b@ e.printStackTrace();@b@ }@b@ return result;@b@ }@b@ @b@ public static void main(String[] args){@b@ @b@ System.out.println(maskSecretLog(new customer("159215401234","小木人印象")));@b@ @b@ System.out.println(maskSecretLog(JSONObject.toJSONString(new customer("159215401235","小木人印象2"))));@b@ @b@ @b@ }@b@ @b@ static class customer{@b@ @b@ private String customerPhone;@b@ @b@ private String customerName;@b@@b@ public customer(String customerPhone, String customerName) {@b@ super();@b@ this.customerPhone = customerPhone;@b@ this.customerName = customerName;@b@ }@b@ @b@ public String getCustomerPhone() {@b@ return customerPhone;@b@ }@b@@b@ public void setCustomerPhone(String customerPhone) {@b@ this.customerPhone = customerPhone;@b@ }@b@@b@ public String getCustomerName() {@b@ return customerName;@b@ }@b@@b@ public void setCustomerName(String customerName) {@b@ this.customerName = customerName;@b@ }@b@ @b@ }@b@@b@}
控制台结果
{"customerName":"小****","customerPhone":"1592****1234"}@b@{"customerName":"小*****","customerPhone":"1592****1235"}