本篇文章小编给大家分享一下Spring aop+反射实现电话号加密代码示例,文章代码介绍的很详细,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看。
加密算法
为什么需要加密呢?就好比战争时期特工在进行传输情报的时候,如果将情报明文直接通过某种媒介传输给同盟人员,那么一旦情报被地方截取,就会酿成大祸。如果将明文通过某种加密算法加密成杂乱无章的密文,即使被敌方截获,没有对应的解密算法,也很难识别出其中的明文。安全传输领域,加密算法是一种很常用的手段,它可以保证数据不被窃取和泄漏,还可以保证数据的完整性,不被篡改。
常见的加密算法有对称加密,非对称加密,单向加密(签名)等分类。其中对称加密算法,加密密钥和解密密钥是同一个,因此发送发和接收方都需要维护一个相同的密钥,如果密钥要修改,双方都需要同时修改。非对称加密算法中,发送发用公钥进行加密,接收方用私钥进行解密。单向加密算法是对传输的数据生成一个签名,通过这个签名来验证数据在传输过程中是否被篡改过,一般是不可逆的。
AES
AES 加密算法是一种对称加密算,加密密钥和解密密钥是同一个。它采用对称分组密码体制,最少支持长度为128位的加密。涉及到分组加密,padding填充,初始向量IV,密钥,四种加密模式。
分组加密就是将原文分割成一段段的分别进行加密,每段分组长度为128位16个字节,如果最后一组长度不足128位,则采用padding填充模式将其补齐到128位。然后对每组进行加密,最后组成最终密文。
padding填充是为了解决分组后的长度不足128位的场景。填充模式也有多种不同模式,比如PKCS5, PKCS7和NOPADDING。其中PKSC5是指分组后缺少几个字节,就在后面填充几个字节的几,比如缺少2个字节,就在后面填充2个字节的2。PKCS7是指缺少几个字节,就在后面填充几个字节的0,比如缺少5个字节,就填充5个字节的0。NOPADDING模式就是不需要填充。如果最后面刚好是16个字节的16,那么解密方不知道是填充数据还是真实数据,因此会在后面再补16个字节的16来区分。
初始向量IV是为了保证数据的安全性,如果我们对同一段内容进行加密后,所生成的密文应该是相同的,那么这样就很容易通过密文分析出哪些段是相同的。比如原文分组后成为ABCADE,加密后的密文是GHIGJK,那么很容易看出那两段内容是相同的。第一个分组在初始加密向量的基础上进行加密,以后的每一个分组都在前一个分组加密的结果为基础进行加密,从而保证了即使相同的原文段,也不会生成相同的密文段。
密钥是加密和解密公用的一个,它一般是128位16个字节长度的随机字符串,分组后的原文都用同一个密钥进行加密。
加密模式包含ECB,CBC, CFB, OFB等四种模式。ECB分别对每个分组进行加密,相同的明文会被加密成相同的密文。CBC模式会使用上一段的加密结果作为加密向量,相同的原文不会被加密成相同的密文。
MD5
MD5算法是一种不可逆的签名算法,对相同的输入通过MD5散列函数处理后,会输出相同的信息。因此MD5可以验证传输的数据是否有被篡改,但是如果窃密者对明文进行了修改后,再使用MD5算法进行散列,接收方将无法判断明文已经被修改了。一般数据库存储用户密码会将密码使用MD5进行处理。
HMAC-MD5
HMAC-MD5由一个H函数和一个密钥组成,一般我们采用的散列函数为Md5或者SHA-1。HMAC-MD5算法就是采用密钥加密+Md5信息摘要的方式形成新的密文。
AOP
众所周知,AOP(面向切面编程)是Spring一个重要特性,它将核心关注点和业务逻辑进行解耦,将业务无关的逻辑提取出来作为公共模块进行处理。它有切点,切面,连接点,通知的概念。切点就是我们可以织入切面的点,切面就是我们要织入的横切逻辑,通知包含前置通知,后置通知,返回通知,异常通知,环绕通知等。
1. 引入依赖
org.projectlombok lombok 1.16.18 org.springframework spring-context 5.2.8.RELEASE org.springframework spring-aop 5.2.8.RELEASE org.aspectj aspectjweaver 1.9.5
2. 业主信息类、业主信息服务类
@Data @AllArgsConstructor public class CustomerInfo { private String name; private String phoneNum; } @Service public class CustomerInfoService { @PhoneEncryptionForList(fields = "phoneNum", clazz = CustomerInfo.class) public ListlistCustomerInfo() { return Arrays.asList( new CustomerInfo("小王", "15112368569"), new CustomerInfo("小李", "13652298565"), new CustomerInfo("小武", "18965653698"), new CustomerInfo("小天", "13192558569") ); } }
3. 电话号加密注解,电话号加密切面类
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface PhoneEncryptionForList { /** * 加密字段 * * @return */ String[] fields(); /** * 加密对象类型 * * @return */ Class> clazz(); } @Aspect @Component public class PhoneEncryptionForListAspect { private static final int PHONE_SIZE = 11; @AfterReturning(value = "@annotation(com.zzq.spring.phone.encryption.PhoneEncryptionForList)", returning = "result") public void doEncrypt(JoinPoint joinPoint, Object result) { if (result == null || !(result instanceof List)) { System.out.println("result class type is not list, annotation is invalid"); return; } List list = (List) result; if (CollectionUtils.isEmpty(list)) { return; } // 获取注解的属性值 Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); PhoneEncryptionForList annotation = method.getAnnotation(PhoneEncryptionForList.class); String[] fields = annotation.fields(); Class> clazz = annotation.clazz(); for (String field : fields) { try { // 反射获取实体类加密字段相应的 set 和 get 方法 char[] chars = field.toCharArray(); chars[0] = (char) (chars[0] - 32); String setMethodName = "set" + new String(chars); String getMethodName = "get" + new String(chars); Method getMethod = clazz.getDeclaredMethod(getMethodName); Method setMethod = clazz.getDeclaredMethod(setMethodName, String.class); for (Object obj : list) { doEncryptPhone(getMethod, setMethod, obj); } } catch (Exception e) { System.out.println(field + " encrypt exception, " + e.getMessage()); continue; } } } private static void doEncryptPhone(Method getMethod, Method setMethod, Object obj) throws Exception { // 反射调用 get 方法 String originalPhone = (String)getMethod.invoke(obj); if (!StringUtils.hasText(originalPhone) || originalPhone.length() != PHONE_SIZE) { System.out.println("phone field value is blank or formal error"); return; } String encryptedPhone = originalPhone.replaceAll("(d{3})d{4}(d{4})","$1****$2"); // 反射调用 set 方法 setMethod.invoke(obj, encryptedPhone); } }
4. 测试类
@Configuration @EnableAspectJAutoProxy @ComponentScan("com.zzq.spring.phone.encryption") public class TestApplication { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(TestApplication.class); CustomerInfoService customerInfoService = ctx.getBean(CustomerInfoService.class); ListafterPhoneEncryptCustomerInfos = customerInfoService.listCustomerInfo(); afterPhoneEncryptCustomerInfos.forEach(customerInfos -> { System.out.println(customerInfos); }); } }
5. 结果和总结
该加密注解只简单实现了电话号加密,注解中还可以新增一些属性扩展注解功能,比如增加加密字段格式加密规则的正则表达式的属性值,这样加密的字段类型(手机号、身份证等)和方式(中间多少位加密、首位加密)就可以根据具体需求处理。
忍者必须死34399账号登录版 最新版v1.0.138v2.0.72
下载勇者秘境oppo版 安卓版v1.0.5
下载忍者必须死3一加版 最新版v1.0.138v2.0.72
下载绝世仙王官方正版 最新安卓版v1.0.49
下载Goat Simulator 3手机版 安卓版v1.0.8.2
Goat Simulator 3手机版是一个非常有趣的模拟游
Goat Simulator 3国际服 安卓版v1.0.8.2
Goat Simulator 3国际版是一个非常有趣的山羊模
烟花燃放模拟器中文版 2025最新版v1.0
烟花燃放模拟器是款仿真的烟花绽放模拟器类型单机小游戏,全方位
我的世界动漫世界 手机版v友y整合
我的世界动漫世界模组整合包是一款加入了动漫元素的素材整合包,
我的世界贝爷生存整合包 最新版v隔壁老王
我的世界MITE贝爷生存整合包是一款根据原版MC制作的魔改整