springboot aop方式实现敏感数据自动加解密
一、前言
在实际项目开发中,可能会涉及到一些敏感信息,那么我们就需要对这些敏感信息进行加密处理,
也就是脱敏,比如像手机号、身份证号等信息。如果我们只是在接口返回后再去做替换处理,则代码会显得非常冗余,那么实际可以通过注解的方式实现数据脱敏。
二、具体实现
1.定义一个标记于方法上的注解。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD) //METHOD 说明该注解只能用在方法上
@Retention(RetentionPolicy.RUNTIME) //RUNTIME 说明该注解在运行时生效
public @interface DataEncryption {}
2.定义一个数据解密的注解。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD) //METHOD 说明该注解只能用在方法上
@Retention(RetentionPolicy.RUNTIME) //RUNTIME 说明该注解在运行时生效
public @interface DataDecryption {}
3.定义一个在字段上,且有值的注解。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.FIELD) //FIELD 说明该注解只能用在字段上
@Retention(RetentionPolicy.RUNTIME) //RUNTIME 说明该注解在运行时生效
public @interface EncryptField {//脱敏枚举值DesensitizationEnum value();
}
4.定义一个在字段上,且有值的注解。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.FIELD) //FIELD 说明该注解只能用在字段上
@Retention(RetentionPolicy.RUNTIME) //RUNTIME 说明该注解在运行时生效
public @interface DecryptField {//脱敏枚举值DesensitizedUtil.DesensitizedType value();
}
5.定义用户返回的实体类。
import com.test.annotation.DecryptField;
import com.test.enums.DesensitizationEnum;
import com.test.utils.AesUtil;import java.lang.reflect.Field;
import lombok.Data;
// 用户信息返回实体类
@Data
public class UserResVo {@DecryptField(DesensitizedUtil.DesensitizedType.CHINESE_NAME)@EncryptFieldprivate String name;@EncryptField@EncryptField(DesensitizedUtil.DesensitizedType.ID_CARD)private String idCard;@EncryptField@EncryptField(DesensitizedUtil.DesensitizedType.MOBILE_PHONE)private String phone;@EncryptField@EncryptField(DesensitizedUtil.DesensitizedType.EMAIL)private String email;
}
6.定义AOP注解实现
import cn.hutool.crypto.digest.DigestUtil;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.Objects;@Aspect
@Component
@Slf4j
public class SecretAopAspect {@Pointcut("@annotation(com.secret.annotation.DataEncryption)")public void encryptAopCut() {}@Pointcut("@annotation(com.secret.annotation.DataDecryption)")public void decryptAopCut() {}/*** @method encryptMethodAop* @desc 加密方法* @Param: joinPoint*/@Around("encryptAopCut()")public Object encryptMethodAop(ProceedingJoinPoint joinPoint) {Object responseObj = null;try {responseObj = joinPoint.proceed();this.handleEncrypt(responseObj);//md5加密,在相应里面设置签名值String md5Data = DigestUtil.md5Hex(new Gson().toJson(responseObj));HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();response.setHeader("md5",md5Data);} catch (Throwable throwable) {throwable.printStackTrace();this.log.error("encryptMethodAop处理出现异常{}", throwable);}return responseObj;}/*** @method decryptMethodAop* @desc 解密方法* @Param: joinPoint*/@Around("decryptAopCut()")public Object decryptMethodAop(ProceedingJoinPoint joinPoint) {Object responseObj = null;try {responseObj = joinPoint.getArgs()[0];//throw new RuntimeException("md5校验失败");this.handleDecrypt(responseObj);//生成md5签名String md5 = DigestUtil.md5Hex(new Gson().toJson(responseObj));System.out.println(md5);//从请求头获取前端传过来的签名String origianlMd5 = "";HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();origianlMd5 = request.getHeader("md5");//比对签名if(md5.equals(origianlMd5)){//方便调试,不比对前端签名responseObj = joinPoint.proceed();}else{throw new Exception("参数的md5校验不同,可能存在篡改行为,请检查!");}} catch (Throwable throwable) {throwable.printStackTrace();log.error("decryptMethodAop处理出现异常{}", throwable);}return responseObj;}/*** @method handleEncrypt* @desc 对实体么个属性进行加密* @Param: requestObj* @return void*/private void handleEncrypt(Object requestObj) throws Exception {if (!Objects.isNull(requestObj)) {Field[] fields = requestObj.getClass().getDeclaredFields();Field[] fieldsCopy = fields;int fieldLength = fields.length;for(int i = 0; i < fieldLength; ++i) {Field field = fieldsCopy[i];boolean hasSecureField = field.isAnnotationPresent(EncryptField.class);if (hasSecureField) {field.setAccessible(true);String plaintextValue = (String)field.get(requestObj);String cipherText = AesUtil.encrypt(plaintextValue);field.set(requestObj, cipherText);}}}}/*** @method handleDecrypt* @desc 对实体么个属性进行解密* @Param: responseObj* @return*/private Object handleDecrypt(Object responseObj) throws Exception {if (Objects.isNull(responseObj)) {return null;} else {Field[] fields = responseObj.getClass().getDeclaredFields();Field[] fieldsCopy = fields;int fieldLength = fields.length;for(int i = 0; i < fieldLength; ++i) {Field field = fieldsCopy[i];boolean hasSecureField = field.isAnnotationPresent(DecryptField.class);if (hasSecureField) {DecryptField decryptField = field.getAnnotation(DecryptField.class);field.setAccessible(true);String encryptValue = (String)field.get(responseObj);encryptValue = AesUtil.decrypt(encryptValue,decryptField.getValue());field.set(responseObj, encryptValue);}}return responseObj;}}}
7.加解密工具类
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;public class AesUtil {// 默认16位 或 128 256位public static String AES_KEY = "EN#qerdfdshbd110";public static AES aes = SecureUtil.aes(AES_KEY.getBytes());public static String encrypt(Object obj) {return aes.encryptHex((String) obj);}public static String decrypt(Object obj, DesensitizedUtil.DesensitizedType desensitizedType) {// 解密String decrypt = decrypt(obj);// 脱敏return DesensitizedUtil.desensitized(decrypt, desensitizedType);}public static String decrypt(Object obj) {return aes.decryptStr((String) obj, CharsetUtil.CHARSET_UTF_8);}
}
8.接着就可以测试了。
相关文章:
springboot aop方式实现敏感数据自动加解密
一、前言 在实际项目开发中,可能会涉及到一些敏感信息,那么我们就需要对这些敏感信息进行加密处理, 也就是脱敏,比如像手机号、身份证号等信息。如果我们只是在接口返回后再去做替换处理,则代码会显得非常冗余…...

RabbitMQ---work消息模型
1、work消息模型 工作队列或者竞争消费者模式 在第一篇教程中,我们编写了一个程序,从一个命名队列中发送并接受消息。在这里,我们将创建一个工作队列,在多个工作者之间分配耗时任务。 工作队列,又称任务队列。主要思…...

GitRedisNginx合集
目录 文件传下载 Git常用命令 Git工作区中文件的状态 远程仓库操作 分支操作 标签操作 idea中使用git 设置git.exe路径 操作步骤 linux配置jdk 安装tomcat 查看是否启动成功 查看tomcat进程 防火墙操作 开放指定端口并立即生效 安装mysql 修改mysql密码 安装lrzsz软…...

系统架构设计师之缓存技术:Redis与Memcache能力比较
系统架构设计师之缓存技术:Redis与Memcache能力比较...

02.sqlite3学习——嵌入式数据库的基本要求和SQLite3的安装
目录 嵌入式数据库的基本要求和SQLite3的安装 嵌入式数据库的基本要求 常见嵌入式数据库 sqlite3简介 SQLite3编程接口模型 ubuntu 22.04下的SQLite安装 嵌入式数据库的基本要求和SQLite3的安装 嵌入式数据库的基本要求 常见嵌入式数据库 sqlite3简介 SQLite3编程接口模…...

AIGC ChatGPT 按年份进行动态选择的动态图表
动态可视化分析的好处与优势: 1. 提高信息理解性:可视化分析使得大量复杂的数据变得易于理解,通过图表、颜色、形状、尺寸等方式,能够直观地表现不同的数据关系和模式。 2. 加快决策速度:数据可视化可以帮助用户更快…...

分布式—雪花算法生成ID
一、简介 1、雪花算法的组成: 由64个Bit(比特)位组成的long类型的数字 0 | 0000000000 0000000000 0000000000 000000000 | 00000 | 00000 | 000000000000 1个bit:符号位,始终为0。 41个bit:时间戳,精确到毫秒级别&a…...

Python语言实现React框架
迷途小书童的 Note 读完需要 6分钟 速读仅需 2 分钟 1 reactpy 介绍 reactpy 是一个用 Python 语言实现的 ReactJS 框架。它可以让我们使用 Python 的方式来编写 React 的组件,构建用户界面。 reactpy 的目标是想要将 React 的优秀特性带入 Python 领域,…...

Netty入门学习和技术实践
Netty入门学习和技术实践 Netty1.Netty简介2.IO模型3.Netty框架介绍4. Netty实战项目学习5. Netty实际应用场景6.扩展 Netty 1.Netty简介 Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具&…...

MySQL详细安装与配置
免安装版的Mysql MySQL关是一种关系数据库管理系统,所使用的 SQL 语言是用于访问数据库的最常用的 标准化语言,其特点为体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,在 Web 应用方面 MySQL 是最好的 RDBMS(Relation…...

裸露土堆识别算法
裸露土堆识别算法首先利用图像处理技术,提取出图像中的土堆区域。裸露土堆识别算法首通过计算土堆中被绿色防尘网覆盖的比例,判断土堆是否裸露。若超过40%的土堆没有被绿色防尘网覆盖,则视为裸露土堆。当我们谈起计算机视觉时,首先…...
说说你对Redux的理解?其工作原理?
文章目录 redux?工作原理如何使用后言 redux? React是用于构建用户界面的,帮助我们解决渲染DOM的过程 而在整个应用中会存在很多个组件,每个组件的state是由自身进行管理,包括组件定义自身的state、组件之间的通信通…...
《基于 Vue 组件库 的 Webpack5 配置》7.路径别名 resolve.alias 和 性能 performance
路径别名 resolve.alias const path require(path);module.exports {resolve: {alias: {"": path.resolve(__dirname, "./src/"),"assets": path.resolve(__dirname, "./src/assets/"),"mixins": path.resolve(__dirname,…...

基于PaddleOCR2.7.0发布WebRest服务测试案例
基于PaddleOCR2.7.0发布WebRest服务测试案例 #WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. #警告:这是一个开发服务器。不要在生产部署中使用它。请改用生产WSGI服务器。 输出结果…...
Solidity 合约安全,常见漏洞 (下篇)
Solidity 合约安全,常见漏洞 (下篇) Solidity 合约安全,常见漏洞 (上篇) 不安全的随机数 目前不可能用区块链上的单一交易安全地产生随机数。区块链需要是完全确定的,否则分布式节点将无法达…...
nodejs根据pdf模板填入中文数据并生成新的pdf文件
导入pdf-lib库和fontkit npm install pdf-lib fs npm install pdf-lib/fontkit 具体代码 const { PDFDocument, StandardFonts } require(pdf-lib); const fs require(fs); const fontkit require(pdf-lib/fontkit) let pdfDoc let font async function fillPdfForm(temp…...

UE4与pycharm联合仿真的调试问题及一些仿真经验
文章目录 ue4与pycharm联合仿真的调试问题前言ue4端的debug过程pycharm端 一些仿真经验小结 ue4与pycharm联合仿真的调试问题 前言 因为在实验中我需要用到py代码输出控制信息给到ue4中,并且希望看到py端和ue端分别在运行过程中的输出以及debug调试。所以…...

【数据分析】波士顿矩阵
波士顿矩阵是一种用于分析市场定位和企业发展战略的管理工具。由美国波士顿咨询集团(Boston Consulting Group)于1970年提出,并以该集团命名。 波士顿矩阵主要基于产品生命周期和市场份额两个维度,将企业的产品或业务分为四个象限…...

sizeof和strlen的对比
文章目录 🚩前言🚩sizeof🚩strlen🚩sizeof和strlen对比 🚩前言 很多小白在学习中,经常将sizeof和strlen弄混了。本篇文章,小编讲解一下sizeof和strlen的区别。🤷♂️ 🚩…...
Flutter系列文章-Flutter 插件开发
在本篇文章中,我们将学习如何开发 Flutter 插件,实现 Flutter 与原生平台的交互。我们将详细介绍插件的开发过程,包括如何创建插件项目、实现方法通信、处理异步任务等。最后,我们还将演示如何将插件打包并发布到 Flutter 社区。 …...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...