AOP实现AES+BASE64加解密
场景如下:
需要对数据库存储的字段,进行加解密的处理。如果都直接写代码的话,那么代码回冗余很多,所以使用AOP+注解去实现。让代码简洁,方便
具体实现如下:
1、依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.20</version>
<scope>compile</scope>
</dependency>
2、相关注解
java
package com.walker.aop.fieldAop.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* author:walker
* time: 2023/12/8
* description: 支持属性加密注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SupportFieldEncrypt {
}
java
package com.walker.aop.fieldAop.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* author:walker
* time: 2023/12/8
* description: 支持属性解密注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SupportFieldDecrypt {
}
java
package com.walker.aop.fieldAop.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* author:walker
* time: 2023/12/8
* description: 属性加密
*/
@Target(value = ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldEncrypt {
/**
* 具体参数查看FieldEncryptTypeEnums枚举
* 默认使用AES+BASE64加密
*/
int type() default 1;
}
java
package com.walker.aop.fieldAop.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* author:walker
* time: 2023/12/8
* description: 属性解密
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldDecrypt {
int type() default 1;
}
3、枚举
目前只整合了AES加密的,后续如果需要,可以自己整合
java
package com.walker.aop.fieldAop.enums;
public enum FieldEncryptTypeEnums {
AES_BASE64(1,"AES+BASE64");
private Integer type;
private String description;
FieldEncryptTypeEnums(Integer type, String description) {
this.type = type;
this.description = description;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
4、AES加解密工具类
java
package com.walker.aop.fieldAop;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import java.nio.charset.StandardCharsets;
/**
* author:walker
* time: 2023/12/7
* description: AES加解密工具
*/
public class MyAesUtils {
// 密钥,可以自己修改,当然这里可以不在这里写死,但是这里为了方便,就直接写死了
private final static String AES_KEY="qu6ciLzMME7slNxj";
private static final AES aes = SecureUtil.aes(AES_KEY.getBytes(StandardCharsets.UTF_8));
public static String encrypt(String data){
return Base64.encode(aes.encrypt(data));
}
public static String decrypt(String data){
return aes.decryptStr(Base64.decode(data));
}
public static void main(String[] args) {
String hello = encrypt("hello");
System.out.println(hello);
String decrypt = decrypt(hello);
System.out.println(decrypt);
}
}
5、切面类
java
package com.walker.aop.fieldAop.aspect;
import cn.hutool.core.util.ReflectUtil;
import com.walker.aop.fieldAop.enums.FieldEncryptTypeEnums;
import com.walker.aop.fieldAop.MyAesUtils;
import com.walker.aop.fieldAop.annotations.FieldDecrypt;
import com.walker.aop.fieldAop.annotations.FieldEncrypt;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
@Slf4j
//1、使用切面注解
@Aspect
@Component
public class FieldAopAspect {
// 2、这里的引用路径需要根据自己项目的包去调整
@Pointcut("@annotation(com.walker.aop.fieldAop.annotations.SupportFieldEncrypt)")
public void supportFieldEncrypt() {};
@Pointcut("@annotation(com.walker.aop.fieldAop.annotations.SupportFieldDecrypt)")
public void supportFieldDecrypt() {};
/**
* author:walker
* time: 2023/12/8
* description: 前置通知 加密处理
*/
// 3、Before代表前置通知
@Before("supportFieldEncrypt()")
public void fieldEncryptProcessor(JoinPoint joinPoint){
//获取参数
Object[] args = joinPoint.getArgs();
if(args==null||args.length==0){
return;
}
for (Object arg : args) {
Class<?> aClass = arg.getClass();
// 获取属性
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
// 获取注解
Annotation[] annotations = field.getAnnotations();
for (Annotation annotation : annotations) {
// 判断是否有FieldEncrypt注解
if(annotation.annotationType().equals(FieldEncrypt.class)){
FieldEncrypt fieldEncrypt= (FieldEncrypt) annotation;
int type = fieldEncrypt.type();
if(type== FieldEncryptTypeEnums.AES_BASE64.getType()){
// 如果有注解,则进行加密处理,并使用反射去设置值
Object fieldValue = ReflectUtil.getFieldValue(arg, field);
if(fieldValue==null) return;
String encrypt = MyAesUtils.encrypt(String.valueOf(fieldValue));
System.out.println("加密后结果:"+encrypt);
ReflectUtil.setFieldValue(arg,field,encrypt);
}
}
}
}
}
}
/**
* 解密注解
*/
// 返回后通知
@AfterReturning(value = "supportFieldDecrypt()",returning = "arg")
public void fieldDecryptProcess(JoinPoint joinPoint, Object arg){
if(arg==null) return;
// arg:代表的是返回的结果
Class<?> aClass = arg.getClass();
// 获取属性
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
Annotation[] annotations = field.getAnnotations();
for (Annotation annotation : annotations) {
// 判断属性是否有FieldDecrypt注解
if(annotation.annotationType().equals(FieldDecrypt.class)){
FieldDecrypt fieldDecrypt= (FieldDecrypt) annotation;
int type = fieldDecrypt.type();
// 如果存在注解,则进行解密并且重新设置回对象中
if(type==FieldEncryptTypeEnums.AES_BASE64.getType()){
Object fieldValue = ReflectUtil.getFieldValue(arg, field);
System.out.println("解密前结果:"+fieldValue);
if(fieldValue==null) return;
String decrypt = MyAesUtils.decrypt(String.valueOf(fieldValue));
System.out.println("解密后结果:"+decrypt);
ReflectUtil.setFieldValue(arg,field,decrypt);
}
}
}
}
}
}
6、测试
- 测试实体类
java
package com.walker.aop.fieldAop;
import com.walker.aop.fieldAop.annotations.FieldDecrypt;
import com.walker.aop.fieldAop.annotations.FieldEncrypt;
import lombok.Data;
@Data
public class FieldAopDemo {
@FieldEncrypt
@FieldDecrypt
private String name;
}
- controller
java
package com.walker.aop.fieldAop;
import com.walker.aop.fieldAop.annotations.SupportFieldDecrypt;
import com.walker.aop.fieldAop.annotations.SupportFieldEncrypt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/fieldAOP")
public class FieldAOPController {
@SupportFieldEncrypt
@GetMapping("/encrypt")
public void encrypt(FieldAopDemo demo){
System.out.println(demo);
return;
}
@SupportFieldDecrypt
@GetMapping("/decrypt")
public FieldAopDemo decrypt(FieldAopDemo demo){
System.out.println(demo);
String name = demo.getName();
String encrypt = MyAesUtils.encrypt(name);
System.out.println(encrypt);
demo.setName(encrypt);
return demo;
}
}
之后使用postman调用结果测试:
加密测试:
http://localhost:8080/fieldAOP/encrypt?name=hello