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

相关推荐
小黄编程快乐屋1 小时前
各个排序算法基础速通万字介绍
java·算法·排序算法
kingwebo'sZone1 小时前
ASP.net WebAPI 上传图片实例(保存显示随机文件名)
后端·asp.net
桑榆肖物1 小时前
一个简单的ASP.NET 一致性返回工具库
后端·asp.net
材料苦逼不会梦到计算机白富美3 小时前
贪心算法-区间问题 C++
java·c++·贪心算法
组态软件4 小时前
web组态软件
前端·后端·物联网·编辑器·html
Peter_chq4 小时前
【计算机网络】多路转接之select
linux·c语言·开发语言·网络·c++·后端·select
小小李程序员7 小时前
LRU缓存
java·spring·缓存
cnsxjean7 小时前
SpringBoot集成Minio实现上传凭证、分片上传、秒传和断点续传
java·前端·spring boot·分布式·后端·中间件·架构
hadage2337 小时前
--- stream 数据流 java ---
java·开发语言
《源码好优多》7 小时前
基于Java Springboot汽配销售管理系统
java·开发语言·spring boot