Java怎么通过注解实现接口输出时数据脱敏(java,开发技术)

时间:2024-05-04 00:10:37 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

Java注解实现接口输出数据脱敏

在后台管理中,对于手机号,身份证,姓名这些数据不允许所有人都能看,这时候我们要对相对数据进行脱敏.

先声明了一个注解

通过对相关接口函数进行声明,以及配置需要脱敏的参数类型SecretTypeEnum,默认脱敏手机号

/***脱敏声明*/@Documented@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceSecretManage{SecretTypeEnum[]value()default{SecretTypeEnum.MOBILE};}

我们目前只支持对手机号

身份证,用户姓名三个字段进行脱敏, 字段名称必须符合枚举的desc值

packagecom.test.base.enums;importcom.fasterxml.jackson.annotation.JsonValue;/***@authorfyf*@date2021/2/26*/publicenumSecretTypeEnumimplementsBaseEnum{MOBILE(0,"mobile"),NAME(1,"name"),ID(2,"identity");@JsonValueprivateintcode;privateStringdesc;publicStringgetDesc(){returndesc;}SecretTypeEnum(intcode,Stringdesc){this.code=code;this.desc=desc;}@Overridepublicintcode(){returncode;}publicstaticSecretTypeEnumcheckParam(StringparamName){SecretTypeEnum[]values=values();for(inti=0;i<values.length;i++){if(paramName.equals(values[i].getDesc())){returnvalues[i];}}returnnull;}}

然后我们需要实现注解的拦截功能

/***对字段进行脱敏管理*@author:fyf*@date:2021/02/23*/@Order(1)@Aspect@ComponentpublicclassSecretManageAspect{@Pointcut("@annotation(com.test.base.annotations.SecretManage)")publicvoidpointCut(){}@Around("pointCut()&&@annotation(secretManage)")publicObjectsecretManageAround(ProceedingJoinPointjoinPoint,SecretManagesecretManage)throwsThrowable{ClassreturnType=((MethodSignature)joinPoint.getSignature()).getReturnType();ObjectinvokeResult=joinPoint.proceed();if(returnType.isInstance(invokeResult)){returnType.cast(invokeResult);}Field[]fields=returnType.getDeclaredFields();List<SecretTypeEnum>annotationSecretTypeEnums=Arrays.asList(secretManage.value());for(Fieldfield:fields){StringfieldName=field.getName();Class<?>paramType=field.getType();System.out.println("字段名称:"+fieldName);SecretTypeEnumsecretTypeEnum=SecretTypeEnum.checkParam(fieldName);longcount=annotationSecretTypeEnums.stream().filter(item->item.equals(secretTypeEnum)).count();if(secretTypeEnum!=null&&count>0){fieldName=fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);//获取到setter方法MethodsetMethod=returnType.getMethod("set"+fieldName,newClass[]{paramType});//获取到getter方法MethodgetMethod=returnType.getMethod("get"+fieldName);//执行getter方法Objectvalue=getMethod.invoke(invokeResult);this.invokeSetter(setMethod,invokeResult,secretTypeEnum,value);}}returninvokeResult;}/***封装执行setter函数*/privatevoidinvokeSetter(MethodsetterMethod,ObjectinvokeResult,SecretTypeEnumsecretTypeEnum,Objectvalue)throwsInvocationTargetException,IllegalAccessException{switch(secretTypeEnum){caseNAME:setterMethod.invoke(invokeResult,SecretUtil.nameSecret(value.toString()));break;caseMOBILE:setterMethod.invoke(invokeResult,SecretUtil.mobileSecret(value.toString()));break;caseID:setterMethod.invoke(invokeResult,SecretUtil.idNoSecret(value.toString()));break;}}}

上面我们就是实现了脱敏的功能,现在我们可以mock接口看看结果了,

我对默认声明和脱敏名称和手机号进行了测试

 /** *curllocalhost:9999/user/test-secret */ @GetMapping("/test-secret")// @SecretManage(value={SecretTypeEnum.NAME,SecretTypeEnum.MOBILE}) @SecretManage publicUsergetSecretUser(){ Useruser=newUser(); user.setId(1); user.setMobile("13715166409"); user.setName("张志新"); user.setIdentity("370283790911703"); returnuser; }

下面是测试结果

Java怎么通过注解实现接口输出时数据脱敏

Java注解的字段脱敏处理

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

定义需要脱敏的字段规则

importjava.lang.reflect.Array;importjava.lang.reflect.Field;importjava.lang.reflect.Method;importjava.util.Collection;importjava.util.HashSet;importjava.util.Iterator;importjava.util.Map;importjava.util.Map.Entry;importjava.util.Set;importorg.apache.commons.lang.ArrayUtils;importorg.apache.commons.lang.StringUtils;importcom.alibaba.fastjson.JSON;importcom.alibaba.fastjson.serializer.SerializerFeature;importcom.google.gson.Gson;importcom.ucf.platform.framework.core.annotation.SensitiveInfo;importcom.ucf.platform.framework.core.log.UcfLogger;importcom.ucf.platform.framework.core.log.UcfLoggerFactory;/***@Title:SensitiveInfoUtils.java*@Copyright:Copyright(c)2011*@Description:<br>*敏感信息屏蔽工具<br>*/publicfinalclassSensitiveInfoUtils{privatefinalstaticUcfLoggerlogger=UcfLoggerFactory.getLogger(SensitiveInfoUtils.class);/***[中文姓名]只显示第一个汉字,其他隐藏为2个星号<例子:李**>**@paramname*@return*/publicstaticStringchineseName(StringfullName){if(StringUtils.isBlank(fullName)){return"";}Stringname=StringUtils.left(fullName,1);returnStringUtils.rightPad(name,StringUtils.length(fullName),"*");}/***[中文姓名]只显示第一个汉字,其他隐藏为2个星号<例子:李**>**@paramfamilyName*@paramgivenName*@return*/publicstaticStringchineseName(StringfamilyName,StringgivenName){if(StringUtils.isBlank(familyName)||StringUtils.isBlank(givenName)){return"";}returnchineseName(familyName+givenName);}/***[身份证号]显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>**@paramid*@return*/publicstaticStringidCardNum(Stringid){if(StringUtils.isBlank(id)){return"";}Stringnum=StringUtils.right(id,4);returnStringUtils.leftPad(num,StringUtils.length(id),"*");}/***[固定电话]后四位,其他隐藏<例子:****1234>**@paramnum*@return*/publicstaticStringfixedPhone(Stringnum){if(StringUtils.isBlank(num)){return"";}returnStringUtils.leftPad(StringUtils.right(num,4),StringUtils.length(num),"*");}/***[手机号码]前三位,后四位,其他隐藏<例子:138******1234>**@paramnum*@return*/publicstaticStringmobilePhone(Stringnum){if(StringUtils.isBlank(num)){return"";}returnStringUtils.left(num,3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num,4),StringUtils.length(num),"*"),"***"));}/***[地址]只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****>**@paramaddress*@paramsensitiveSize*敏感信息长度*@return*/publicstaticStringaddress(Stringaddress,intsensitiveSize){if(StringUtils.isBlank(address)){return"";}intlength=StringUtils.length(address);returnStringUtils.rightPad(StringUtils.left(address,length-sensitiveSize),length,"*");}/***[电子邮箱]邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com>**@paramemail*@return*/publicstaticStringemail(Stringemail){if(StringUtils.isBlank(email)){return"";}intindex=StringUtils.indexOf(email,"@");if(index<=1)returnemail;elsereturnStringUtils.rightPad(StringUtils.left(email,1),index,"*").concat(StringUtils.mid(email,index,StringUtils.length(email)));}/***[银行卡号]前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234>**@paramcardNum*@return*/publicstaticStringbankCard(StringcardNum){if(StringUtils.isBlank(cardNum)){return"";}returnStringUtils.left(cardNum,6).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum,4),StringUtils.length(cardNum),"*"),"******"));}/***[公司开户银行联号]公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号<例子:12********>**@paramcode*@return*/publicstaticStringcnapsCode(Stringcode){if(StringUtils.isBlank(code)){return"";}returnStringUtils.rightPad(StringUtils.left(code,2),StringUtils.length(code),"*");}/***获取脱敏json串<注意:递归引用会导致java.lang.StackOverflowError>**@paramjavaBean*@return*/publicstaticStringgetJson(ObjectjavaBean){Stringjson=null;if(null!=javaBean){Class<?extendsObject>raw=javaBean.getClass();try{if(raw.isInterface())returnjson;Gsong=newGson();Objectclone=g.fromJson(g.toJson(javaBean,javaBean.getClass()),javaBean.getClass());Set<Integer>referenceCounter=newHashSet<Integer>();SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(raw),clone,referenceCounter);json=JSON.toJSONString(clone,SerializerFeature.WriteMapNullValue,SerializerFeature.WriteNullListAsEmpty);referenceCounter.clear();referenceCounter=null;}catch(Throwablee){logger.error("SensitiveInfoUtils.getJson()ERROR",e);}}returnjson;}privatestaticField[]findAllField(Class<?>clazz){Field[]fileds=clazz.getDeclaredFields();while(null!=clazz.getSuperclass()&&!Object.class.equals(clazz.getSuperclass())){fileds=(Field[])ArrayUtils.addAll(fileds,clazz.getSuperclass().getDeclaredFields());clazz=clazz.getSuperclass();}returnfileds;}privatestaticvoidreplace(Field[]fields,ObjectjavaBean,Set<Integer>referenceCounter)throwsIllegalArgumentException,IllegalAccessException{if(null!=fields&&fields.length>0){for(Fieldfield:fields){field.setAccessible(true);if(null!=field&&null!=javaBean){Objectvalue=field.get(javaBean);if(null!=value){Class<?>type=value.getClass();//1.处理子属性,包括集合中的if(type.isArray()){intlen=Array.getLength(value);for(inti=0;i<len;i++){ObjectarrayObject=Array.get(value,i);SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(arrayObject.getClass()),arrayObject,referenceCounter);}}elseif(valueinstanceofCollection<?>){Collection<?>c=(Collection<?>)value;Iterator<?>it=c.iterator();while(it.hasNext()){ObjectcollectionObj=it.next();SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(collectionObj.getClass()),collectionObj,referenceCounter);}}elseif(valueinstanceofMap<?,?>){Map<?,?>m=(Map<?,?>)value;Set<?>set=m.entrySet();for(Objecto:set){Entry<?,?>entry=(Entry<?,?>)o;ObjectmapVal=entry.getValue();SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(mapVal.getClass()),mapVal,referenceCounter);}}elseif(!type.isPrimitive()&&!StringUtils.startsWith(type.getPackage().getName(),"javax.")&&!StringUtils.startsWith(type.getPackage().getName(),"java.")&&!StringUtils.startsWith(field.getType().getName(),"javax.")&&!StringUtils.startsWith(field.getName(),"java.")&&referenceCounter.add(value.hashCode())){SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(type),value,referenceCounter);}}//2.处理自身的属性SensitiveInfoannotation=field.getAnnotation(SensitiveInfo.class);if(field.getType().equals(String.class)&&null!=annotation){StringvalueStr=(String)value;if(StringUtils.isNotBlank(valueStr)){switch(annotation.type()){caseCHINESE_NAME:{field.set(javaBean,SensitiveInfoUtils.chineseName(valueStr));break;}caseID_CARD:{field.set(javaBean,SensitiveInfoUtils.idCardNum(valueStr));break;}caseFIXED_PHONE:{field.set(javaBean,SensitiveInfoUtils.fixedPhone(valueStr));break;}caseMOBILE_PHONE:{field.set(javaBean,SensitiveInfoUtils.mobilePhone(valueStr));break;}caseADDRESS:{field.set(javaBean,SensitiveInfoUtils.address(valueStr,4));break;}caseEMAIL:{field.set(javaBean,SensitiveInfoUtils.email(valueStr));break;}caseBANK_CARD:{field.set(javaBean,SensitiveInfoUtils.bankCard(valueStr));break;}caseCNAPS_CODE:{field.set(javaBean,SensitiveInfoUtils.cnapsCode(valueStr));break;}}}}}}}}//----------------------------------------------------------------------------------------------publicstaticMethod[]findAllMethod(Class<?>clazz){Method[]methods=clazz.getMethods();returnmethods;}//----------------------------------------------------------------------------------------------publicstaticenumSensitiveType{/***中文名*/CHINESE_NAME,/***身份证号*/ID_CARD,/***座机号*/FIXED_PHONE,/***手机号*/MOBILE_PHONE,/***地址*/ADDRESS,/***电子邮件*/EMAIL,/***银行卡*/BANK_CARD,/***公司开户银行联号*/CNAPS_CODE;}}

声明注解

importjava.lang.annotation.Documented;importjava.lang.annotation.ElementType;importjava.lang.annotation.Inherited;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;importcom.ucf.platform.framework.core.util.SensitiveInfoUtils;/***@Title:SensitiveInfo.java*@Copyright:Copyright(c)2015*@Description:<br>*敏感信息注解标记<br>*/@Target({ElementType.FIELD,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic@interfaceSensitiveInfo{publicSensitiveInfoUtils.SensitiveTypetype();}

测试

publicclassJavaBeanA{publicJavaBeanA(Stringname,Stringid){}@SensitiveInfo(type=SensitiveType.CHINESE_NAME)privateStringname="A先生";privateJavaBeanBb;privateDatedate;privateList<JavaBeanB>list;privateMap<String,JavaBeanB>map;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicJavaBeanBgetB(){returnb;}publicvoidsetB(JavaBeanBb){this.b=b;}publicList<JavaBeanB>getList(){returnlist;}publicvoidsetList(List<JavaBeanB>list){this.list=list;}publicMap<String,JavaBeanB>getMap(){returnmap;}publicvoidsetMap(Map<String,JavaBeanB>map){this.map=map;}publicDategetDate(){returndate;}publicvoidsetDate(Datedate){this.date=date;}}
publicclassJavaBeanB{@SensitiveInfo(type=SensitiveType.CHINESE_NAME)privateStringname="B先生";privateJavaBeanAa;privateSet<JavaBeanA>list;privateMap<String,JavaBeanA>map;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicJavaBeanAgetA(){returna;}publicvoidsetA(JavaBeanAa){this.a=a;}publicSet<JavaBeanA>getList(){returnlist;}publicvoidsetList(Set<JavaBeanA>list){this.list=list;}publicMap<String,JavaBeanA>getMap(){returnmap;}publicvoidsetMap(Map<String,JavaBeanA>map){this.map=map;}}
publicclassSensitiveInfoUtilsTest{/***[中文姓名]只显示第一个汉字,其他隐藏为2个星号<例子:李**>*/@TestpublicvoidtestChineseNameString(){System.out.println(SensitiveInfoUtils.chineseName("李先生"));}/***[中文姓名]只显示第一个汉字,其他隐藏为2个星号<例子:李**>*/@TestpublicvoidtestChineseNameStringString(){System.out.println(SensitiveInfoUtils.chineseName("李","雷"));}/***[身份证号]显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>*/@TestpublicvoidtestIdCardNum(){System.out.println(SensitiveInfoUtils.idCardNum("1103541983073188711"));}/***[固定电话]后四位,其他隐藏<例子:****1234>*/@TestpublicvoidtestFixedPhone(){System.out.println(SensitiveInfoUtils.fixedPhone("01077482277"));}/***[手机号码]前三位,后四位,其他隐藏<例子:138******1234>*/@TestpublicvoidtestMobilePhone(){System.out.println(SensitiveInfoUtils.mobilePhone("13777446578"));}/***[地址]只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****>*/@TestpublicvoidtestAddress(){System.out.println(SensitiveInfoUtils.address("北京朝阳区酒仙桥中路26号院4号楼人人大厦",8));}/***[电子邮箱]邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com>*/@TestpublicvoidtestEmail(){System.out.println(SensitiveInfoUtils.email("66374777@qq.com"));}/***[银行卡号]前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234>*/@TestpublicvoidtestBankCard(){System.out.println(SensitiveInfoUtils.bankCard("6228480402565890018"));}/***[公司开户银行联号]公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号<例子:12********>*/@TestpublicvoidtestCnapsCode(){System.out.println(SensitiveInfoUtils.cnapsCode("102100029679"));}/***获取脱敏json串<注意:递归引用会导致java.lang.StackOverflowError>*/@TestpublicvoidtestGetJson(){//ThreadPoolExecutorconsumeExecutor=newThreadPoolExecutor(30,30+10,5,TimeUnit.SECONDS,newArrayBlockingQueue<Runnable>(30+10),newThreadFactory(){//@Override//publicThreadnewThread(Runnabler){//ThreadmyThread=newThread(r);//myThread.setName("TT");//returnmyThread;//}//},newThreadPoolExecutor.CallerRunsPolicy());//while(true){//consumeExecutor.execute(newRunnable(){//@Override//publicvoidrun(){}//});//}JavaBeanAa1=newJavaBeanA("","");JavaBeanAa2=newJavaBeanA("","");JavaBeanBb1=newJavaBeanB();a1.setB(b1);//a1.setDate(newDate());List<JavaBeanB>a1l=newArrayList<JavaBeanB>();a1l.add(b1);a1.setList(a1l);Map<String,JavaBeanB>a1m=newHashMap<String,JavaBeanB>();a1m.put("b1",b1);a1.setMap(a1m);b1.setA(a2);Set<JavaBeanA>b1l=newHashSet<JavaBeanA>();b1.setList(b1l);Map<String,JavaBeanA>b1m=newHashMap<String,JavaBeanA>();b1m.put("a2",a2);b1.setMap(b1m);longt=System.currentTimeMillis();System.out.println(t);System.out.println(SensitiveInfoUtils.getJson(a1));System.out.println(System.currentTimeMillis()-t);System.out.println(JSON.toJSON(a1));System.out.println(System.currentTimeMillis()-t);}}

测试结果:

李**
李*
***************8711
*******2277
137****6578
北京朝阳区酒仙桥中路26号********
6*******@qq.com
622848*********0018
10**********
1443435915750
{"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**"}
289
{"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先生"}
300

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

<!--gson--><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId></dependency>

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

 </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
本文:Java怎么通过注解实现接口输出时数据脱敏的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:Assert.assertEquals()方法参数的示例分析下一篇:

7 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18