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

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
    @FieldDecrypt
    private 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
image.png

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/230165.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序 bindtap 事件多参数传递

在微信小程序中&#xff0c;我们无法直接通过 bindtap"handleClick(1,2,3)" 的方式传递参数&#xff0c;而是需要通过自定义属性 data- 的方式进行传递&#xff0c;并在事件回调函数中通过 event.currentTarget.dataset 来获取这些参数。然而&#xff0c;这种传参方式…

SpringAOP专栏二《原理篇》

上一篇SpringAOP专栏一《使用教程篇》-CSDN博客介绍了SpringAop如何使用&#xff0c;这一篇文章就会介绍Spring AOP 的底层实现原理&#xff0c;并通过源代码解析来详细阐述其实现过程。 前言 Spring AOP 的实现原理是基于动态代理和字节码操作的。不了解动态代理和字节码操作…

1、关于前端js-ajax绕过

1、Ajax知识 、js--Ajax 传统请求跟js--Ajax请求的差别 在实例中用的上js-ajax的有 表单验证&#xff1a; 在用户填写表单时&#xff0c;可以使用 Ajax 在不刷新页面的情况下验证表单字段&#xff0c;并提供即时反馈。 实时搜索&#xff1a; 在搜索框中输入内容时&#xff0…

现代皮质沙发模型材质编辑

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 当谈到游戏角色的3D模型风格时&#xff0c;有几种不同的风格&#xf…

元宇宙vr党建云上实景展馆扩大党的影响力

随着科技的飞速发展&#xff0c;VR虚拟现实技术已经逐渐融入我们的日常生活&#xff0c;尤其在党建领域&#xff0c;VR数字党建展馆更是成为引领红色教育新风尚的重要载体。今天&#xff0c;就让我们一起探讨VR数字党建展馆如何提供沉浸式体验&#xff0c;助力党建工作创新升级…

Mybatis XML 多表查询

这篇需结合 <<Mybatis XML 配置文件>>那一篇博客一起看 工作中尽量避免使用多表查询,尤其是对性能要求非常高的项目 我们之前建了个用户表(代码在Mybatis XML配置文件那篇博客里),这次再建一个文章表,代码如下 : -- 创建⽂章表 DROP TABLE IF EXISTS articleinf…

LabVIEW开发远程结构健康监测系统

LabVIEW开发远程结构健康监测系统 工程师依赖于振动监测来评估建筑物、桥梁和其他大型结构的完整性。传统的振动监测工具在数据收集上存在限制&#xff0c;无法长时间收集高保真波形。随着内存存储、处理器速度和宽带无线通信技术的进步&#xff0c;出现了对能够长时间收集并实…

【链表Linked List】力扣-203 移除链表元素

目录 题目描述 解题过程 题目描述 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5…

CGAL的3D简单网格数据结构

由具有多个曲面面片的多面体曲面生成的多域四面体网格。将显示完整的三角剖分&#xff0c;包括属于或不属于网格复合体、曲面面片和特征边的单元。 1、网格复合体、 此软件包致力于三维单纯形网格数据结构的表示。 一个3D单纯形复杂体由点、线段、三角形、四面体及其相应的组合…

【c语言指针详解】指针的基本概念和用法

目录 一、指针的基本概念和用法 二、指针运算 2.1 指针的自增和自减运算 2.2 指针的自增和自减运算 三、数组和指针 四、指针和函数 4.1 在函数中使用指针作为参数和返回值 4.1.1 使用指针作为函数参数 4.1.2 使用指针作为函数返回值 4.2 指针参数的传值和传引用特性 4.2.1 指针…

element中el-select多选v-model是对象数组

文章目录 一、问题二、解决三、最后 一、问题 element中的el-select的v-model一般都是字符串或者字符串数组&#xff0c;但是有些时候后端接口要求该字段要传对象或者对象数组&#xff0c;如果再转换一次数据&#xff0c;对于保存配置和回显都是吃力不讨好的事情。如下所示&am…

【投稿】期刊选择

一、期刊影响力评价方法 只要投稿的期刊&#xff0c;被上述三个索引收录&#xff0c;那就说明该期刊的影响力是得到认可的。 二、如何选择合适的期刊 研究工作和目标期刊进行权衡。

直面双碳目标,优维科技携手奥意建筑打造绿色低碳建筑数智云平台

优维“双碳”战略合作建筑 为落实创新驱动发展战略&#xff0c;增强深圳工程建设领域科技创新能力&#xff0c;促进技术进步、科技成果转化和推广应用&#xff0c;根据《深圳市工程建设领域科技计划项目管理办法》《深圳市住房和建设局关于组织申报2022年深圳市工程建设领域科…

pytorch的二次索引矩阵无法赋值问题

最近在研究中发现torch一个问题&#xff0c;即torch的二次索引的矩阵无法赋值。 具体来说&#xff0c;给定相同的初始常数矩阵a和iou_target矩阵, 以及另一iou矩阵&#xff0c;直接赋值是没问题的。 然而&#xff0c;当对iou_target矩阵进行二次索引时&#xff0c;即idx矩阵和…

IntelliJ IDEA安装

文章目录 IntelliJ IDEA安装说明下载执行安装 IntelliJ IDEA安装 说明 操作系统&#xff1a;windows10 版本&#xff1a;2020.1 下载 官网地址 执行安装

android项目实战之编辑器集成

引言 项目需要用到编辑器&#xff0c;采用RichEditor&#xff0c;如下效果 实现 1. 引入库2 implementation jp.wasabeef:richeditor-android:2.0.0 2. XML <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width&q…

cache 2.单机并发缓存

0.对原教程的一些见解 个人认为原教程中两点知识的引入不够友好。 首先是只读数据结构 ByteView 的引入使用是有点迷茫的&#xff0c;可能不能很好理解为什么需要ByteView。 第二是主体结构 Group的引入也疑惑。其实要是熟悉groupcache&#xff0c;那对结构Group的使用是清晰…

springboot3远程调用

RPC 两个服务器之间的调用 远程请求 内部服务之间的调用 可以通过 cloud 注册中心 openfeign等 外部服务的调用 http请求 外部协议 api:远程接口 sdk&#xff1a;本地调用 调用阿里云的天气请求

hbuilder + uniapp +vue3 开发微信云小程序

1、创建项目&#xff1a; 2、创建项目完成的默认目录结构&#xff1a; 3、在根目录新建一个文件夹cloudFns&#xff08;文件名字随便&#xff09;&#xff0c;存放云函数源码&#xff1a; 4、修改manifest.json文件&#xff1a;添加 小程序 appid和cloudfunctionRoot&#xff0…

【EI会议征稿中】2024年第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)

2024年第四届人工智能、自动化与高性能计算国际会议&#xff08;AIAHPC 2024&#xff09; 2024 4th International Conference on Artificial Intelligence, Automation and High Performance Computing 2024第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)将于20…