Springboot+AOP+注解实现字段AES+Base64加解密

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

解密测试:
http://localhost:8080/fieldAOP/decrypt?name=hello

相关推荐
独断万古他化6 分钟前
【Java 实战项目】多用户网页版聊天室:消息传输模块 —— 基于 WebSocket 实现实时通信
java·spring boot·后端·websocket·ajax·mybatis
舒一笑10 分钟前
🚀 我用一行命令,把 OSS 私有文件变成“可直接下载的公网链接”(很多人不会)
后端
yyt36304584112 分钟前
spring单例bean线程安全问题讨论
java·spring
小兔崽子去哪了22 分钟前
Docker 安装 PostgreSQL
数据库·后端·postgresql
Sweet锦23 分钟前
SpringBoot 3.5 集成 InfluxDB 1.8
spring boot·时序数据库
野犬寒鸦27 分钟前
Redis热点key问题解析与实战解决方案(附大厂实际方案讲解)
服务器·数据库·redis·后端·缓存·bootstrap
我是大猴子33 分钟前
事务失效的几种情况以及是为什么(详解)
java·开发语言
snakeshe10101 小时前
深入理解 Java 注解:从原理到实战
后端
Lucaju1 小时前
吃透 Spring AI Alibaba 多智能体|四大协同模式+完整代码
后端
Nyarlathotep01131 小时前
Redis的对象(5):有序集合对象
redis·后端