springboot使用自定义注解实现加解密及脱敏
原文链接: https://www.yisu.com/zixun/622633.html
这篇文章主要介绍springboot中如何使用自定义注解实现加解密及脱敏方式,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
自定义注解实现加解密及脱敏
定义自定义注解
1 2 3 4 5 6 | @Documented @Target ({ElementType.FIELD}) @Retention (RetentionPolicy.RUNTIME) @Order (Ordered.HIGHEST_PRECEDENCE) public @interface PrivateData { } |
1 2 3 4 5 6 | @Documented @Target ({ElementType.METHOD}) @Retention (RetentionPolicy.RUNTIME) @Order (Ordered.HIGHEST_PRECEDENCE) public @interface PrivateDataMethod { } |
首先定义两个自定义注解,privateData和privateDataMethod,分别将@Target属性定义为FIELD和METHOD。
构造AOP逻辑
申明一个切入点
1 2 3 | @Pointcut ( "@annotation(com.max.base.services.annotation.PrivateDataMethod)" ) public void annotationPointCut() { } |
对所有添加@privateDataMethod注解的方法进行切入。
申明通知
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | @Around ( "annotationPointCut()" ) public Object around(ProceedingJoinPoint joinPoint) { Object responseObj = null ; try { Object[] request = joinPoint.getArgs(); for (Object object : request) { if (object instanceof Collection) { Collection collection = (Collection) object; collection.forEach(var -> { try { handleEncrypt(var); } catch (IllegalAccessException e) { e.printStackTrace(); } }); } else { handleEncrypt(object); } } responseObj = joinPoint.proceed(); if (responseObj instanceof Collection) { Collection collection = (Collection) responseObj; collection.forEach(var -> { try { handleDecrypt(var); } catch (IllegalAccessException e) { e.printStackTrace(); } }); } else { handleDecrypt(responseObj); } } catch (Throwable throwable) { throwable.printStackTrace(); log.error( "SecureFieldAop 异常{}" , throwable); } return responseObj; } |
申明Aroud通知,对于方法输入输出的对象进行判断,如果是非集合对象则直接进行加解密操作,否则则拆分集合,逐一操作
处理加解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /** * 处理加密 * @param requestObj */ private void handleEncrypt(Object requestObj) throws IllegalAccessException { if (Objects.isNull(requestObj)) { return ; } Field[] fields = requestObj.getClass().getDeclaredFields(); for (Field field : fields) { boolean hasSecureField = field.isAnnotationPresent(PrivateData. class ); if (hasSecureField) { Boolean accessible = field.isAccessible(); if (!accessible) { field.setAccessible( true ); } String plaintextValue = (String) field.get(requestObj); String encryptValue = AseUtil.encrypt(plaintextValue, secretKey); field.set(requestObj, encryptValue); if (!accessible) { field.setAccessible( false ); } } } } |
通过反射获取对象的Field列表,对于拥有@PrivateData注解的字段执行**encryptValue()**方法并用加密后的字符串覆盖原字段。
解密逻辑与加密类似,不做赘述。
测试
标识insert()方法为需要加密的方法
1 2 3 4 5 6 7 8 9 | public interface CmTenantMapper { int deleteByPrimaryKey(Long id); @PrivateDataMethod int insert(CmTenant record); int insertSelective(CmTenant record); CmTenant selectByPrimaryKey(Long id); int updateByPrimaryKeySelective(CmTenant record); int updateByPrimaryKey(CmTenant record); } |
对传入对象中需要加密的字段添加注解
1 2 3 4 5 6 7 8 9 10 11 12 | public class CmTenant { private Long id; private String tenantId; @PrivateData private String tenantName; private String createBy; private Date createDate; private String updateBy; private Date updateDate; private String remarks; private Byte delFlag; //set get... |
调用insert方法查看数据保存结果
传入对象
1 2 3 4 5 6 7 8 | { "createBy" : "可乐可不乐" , "delFlag" : "NOTDELETE" , "remarks" : "测试加密" , "tenantId" : "996" , "tenantName" : "椅子团队出品" , "updateBy" : "可乐可不乐" } |
数据库保存对象
解密测试不做注释,大家自行尝试
脱敏逻辑
脱敏逻辑与加解密基本一致,需要注意的一点是脱敏的注解需要添加type类型
1 2 3 4 5 6 7 | @Documented @Target ({ElementType.FIELD}) @Retention (RetentionPolicy.RUNTIME) @Order (Ordered.HIGHEST_PRECEDENCE) public @interface MaskingField { MaskingTypeEnum type(); } |
在MaskingTypeEnum中定义脱敏的分类
1 2 3 4 5 6 7 8 9 10 | public enum MaskingTypeEnum { /*身份证号码*/ ID_CARD, /*手机号码*/ PHONE, /*地址*/ ADDRESS, /*姓名*/ NAME } |
在使用是MaskingTypeEnum时标识字段的类型
1 2 | @MaskingField (type = MaskingTypeEnum.NAME) private String cpName; |
自定义一种字符串的加密与解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package com.hanqi.lianxi; package com.hanqi.lianxi; import java.util.Scanner; public class jiamiqi { public static void main(String[] args) { //输入解密前的内容 Scanner sc = new Scanner(System.in); System.out.println( "请输入需要解码的信件内容" ); String tex = sc.nextLine(); //用替换的方式实现解密 System.out.print( "解密后的内容为:" +tex.replaceAll( "A" , "c" ).replaceAll( "B" , "d" ).replaceAll( "C" , "e" ).replaceAll( "D" , "f" ).replaceAll( "E" , "g" ).replaceAll( "F" , "h" ).replaceAll( "G" , "i" ).replaceAll( "H" , "j" ).replaceAll( "I" , "k" ).replaceAll( "J" , "l" ).replaceAll( "K" , "m" ).replaceAll( "L" , "n" ).replaceAll( "M" , "o" ).replaceAll( "N" , "p" ).replaceAll( "O" , "q" ).replaceAll( "P" , "r" ).replaceAll( "Q" , "s" ).replaceAll( "R" , "t" ).replaceAll( "S" , "u" ).replaceAll( "T" , "v" ).replaceAll( "U" , "w" ).replaceAll( "V" , "x" ).replaceAll( "W" , "y" ).replaceAll( "X" , "z" ).replaceAll( "Y" , "a" ).replaceAll( "Z" , "b" )); } } |
分类:
springboot
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
2018-10-13 SpringBoot实现JWT保护前后端分离RESTful API
2018-10-13 springboot mybatis自定义枚举enum转换