AOP实现AES+BASE64加解密
场景如下:
需要对数据库存储的字段,进行加解密的处理。如果都直接写代码的话,那么代码回冗余很多,所以使用AOP+注解去实现。让代码简洁,方便
具体实现如下:
1、依赖
<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、相关注解
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 {
}
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 {
}
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;
}
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加密的,后续如果需要,可以自己整合
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加解密工具类
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、切面类
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、测试
- 测试实体类
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@FieldDecryptprivate String name;
}
- controller
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
解密测试:
http://localhost:8080/fieldAOP/decrypt?name=hello