加密算法
为什么需要加密呢?就好比战争时期特工在进行传输情报的时候,如果将情报明文直接通过某种媒介传输给同盟人员,那么一旦情报被地方截取,就会酿成大祸。如果将明文通过某种加密算法加密成杂乱无章的密文,即使被敌方截获,没有对应的解密算法,也很难识别出其中的明文。安全传输领域,加密算法是一种很常用的手段,它可以保证数据不被窃取和泄漏,还可以保证数据的完整性,不被篡改。
常见的加密算法有对称加密,非对称加密,单向加密(签名)等分类。其中对称加密算法,加密密钥和解密密钥是同一个,因此发送发和接收方都需要维护一个相同的密钥,如果密钥要修改,双方都需要同时修改。非对称加密算法中,发送发用公钥进行加密,接收方用私钥进行解密。单向加密算法是对传输的数据生成一个签名,通过这个签名来验证数据在传输过程中是否被篡改过,一般是不可逆的。

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. 引入依赖
<dependency>
<groupid>org.projectlombok</groupid>
<artifactid>lombok</artifactid>
<version>1.16.18</version>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-context</artifactid>
<version>5.2.8.release</version>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-aop</artifactid>
<version>5.2.8.release</version>
</dependency>
<dependency>
<groupid>org.aspectj</groupid>
<artifactid>aspectjweaver</artifactid>
<version>1.9.5</version>
</dependency>
2. 业主信息类、业主信息服务类
@data
@allargsconstructor
public class customerinfo {
private string name;
private string phonenum;
}
@service
public class customerinfoservice {
@phoneencryptionforlist(fields = "phonenum", clazz = customerinfo.class)
public list<customerinfo> listcustomerinfo() {
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);
list<customerinfo> afterphoneencryptcustomerinfos = customerinfoservice.listcustomerinfo();
afterphoneencryptcustomerinfos.foreach(customerinfos -> {
system.out.println(customerinfos);
});
}
}
5. 结果和总结

该加密注解只简单实现了电话号加密,注解中还可以新增一些属性扩展注解功能,比如增加加密字段格式加密规则的正则表达式的属性值,这样加密的字段类型(手机号、身份证等)和方式(中间多少位加密、首位加密)就可以根据具体需求处理。
以上就是spring aop+反射实现电话号加密的详细内容,更多关于spring aop电话号加密的资料请关注其它相关文章!
漫步在雨中63175441