Springboot 框架中加解密字段后存储数据库
为防止数据库泄露,表里的敏感字段被曝光,需要对用户的重要数据做加密存取。
选择加密算法: 首先,你需要选择适合你的需求的加密算法。一些常见的加密算法包括AES、RSA、SHA等。具体的选择取决于你要加密的数据和安全需求。
引入加密库: 在你的Spring Boot项目中,引入适用于你所选加密算法的库。例如,如果你选择使用AES加密,可以引入Java的javax.crypto库。
创建加密服务: 创建一个用于加密和解密的服务类。这个服务类应该包含加密和解密方法,并将其注入到Spring容器中以供使用。
定义数据库实体类: 在数据库实体类中,将需要加密的字段定义为加密前的原始字段。在将数据保存到数据库之前,使用加密服务对这些字段进行加密。
加密和解密数据: 在业务逻辑中,调用加密服务对需要加密的数据进行加密,并在需要时进行解密。确保加密密钥的安全存储,不要将密钥硬编码在代码中。
定义接口文件--------------------------------------------------
DecryptField.java :
package xxx.xxx
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
//解密字段注解
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DecryptField {
}
EncryptField .java :
//解密字段注解
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
}
NeedDecrypt .java :
//作用于对返回值进行解密处理的方法上
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedDecrypt {
}
NeedEncrypt .java :
//作用于对参数进行加密处理的方法上
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedEncrypt {
}
AesSupport.java 加密解密算法------------------------------------------
public class AesSupport{
private static final String KEY_ALGORITHM = "AES";
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
private static final String AES_KEY = "you aes key"; // 16/24/32-character secret key
private String password;
private SecretKeySpec secretKeySpec;
private static AesSupport instance;
// 私有构造函数,防止外部创建实例
private AesSupport() {
}
// 提供获取单例实例的方法,使用双重检查锁定保证线程安全
public static AesSupport getInstance() throws NoSuchAlgorithmException {
if (instance == null) {
synchronized (AesSupport.class) {
if (instance == null) {
instance = new AesSupport(AES_KEY);
}
}
}
return instance;
}
public AesSupport(String password) throws NoSuchAlgorithmException {
if(StringUtils.isEmpty(password)){
throw new IllegalArgumentException("password should not be null!");
}
this.password = password;
this.secretKeySpec = getSecretKey(password);
}
public String encrypt(String value){
try{
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] content = value.getBytes("UTF-8");
byte[] encryptData = cipher.doFinal(content);
return Hex.bytesToHexString(encryptData);
}catch (Exception e){
throw new IllegalStateException("AES encrypt "+e.getMessage(),e);
}
}
public String decrypt(String value){
if (StringUtils.isEmpty(value)) {
return "";
}
try {
byte[] encryptData = Hex.hexStringToBytes(value);
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] content = cipher.doFinal(encryptData);
return new String(content, "UTF-8");
}catch (Exception e){
throw new IllegalStateException("AES decrypt "+e.getMessage(),e);
}
}
private SecretKeySpec getSecretKey(final String password) throws NoSuchAlgorithmException{
KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.setSeed(password.getBytes());
kg.init(128, random);
SecretKey secretKey = kg.generateKey();
return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);
}
}
DecryptAop.java 解密Aop文件:
@Component
@Aspect
public class DecryptAop {
/**
* 定义需要解密的切入点
*/
@Pointcut(value = "@annotation(xxx..service.crypt.NeedDecrypt)")
public void pointcut() {
}
/**
* 命中的切入点时的环绕通知
*
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//执行目标方法
Object result = proceedingJoinPoint.proceed();
//判断目标方法的返回值类型
if (result instanceof List) {
for (Object tmp : ((List) result)) {
//数据脱敏处理逻辑
this.deepProcess(tmp);
}
} else {
this.deepProcess(result);
}
//log.info("//环绕通知 end");
return result;
}
public void deepProcess(Object obj) throws IllegalAccessException {
if (obj != null) {
//取出输出对象的所有字段属性,并遍历
Field[] declaredFields = obj.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
//判断字段属性上是否标记DecryptField注解
if (declaredField.isAnnotationPresent(DecryptField.class)) {
//如果判断结果为真,则取出字段属性数据进行解密处理
declaredField.setAccessible(true);
Object valObj = declaredField.get(obj);
if (valObj != null) {
String value = valObj.toString();
//加密数据的解密处理
try {
value = AesSupport.getInstance().decrypt(value);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DecryptField annotation = declaredField.getAnnotation(DecryptField.class);
//boolean open = annotation.open();
//把解密后的数据重新赋值
declaredField.set(obj, value);
}
}
}
}
}
}
EncryptAop.java 加密Aop文件:
@Component
@Aspect
public class EncryptAop {
/**
* 定义加密切入点
*/
@Pointcut(value = "@annotation(xxx.service.crypt.NeedEncrypt)")
public void pointcut() {
}
/**
* 命中加密切入点的环绕通知
*
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//获取命中目标方法的入参数
Object[] args = proceedingJoinPoint.getArgs();
if (args.length > 0) {
for (Object arg : args) {
//按参数的类型进行判断,如果业务中还有其他的类型,可酌情增加
if (arg != null) {
if (arg instanceof List) {
for (Object tmp : ((List) arg)) {
//加密处理
this.deepProcess(tmp);
}
} else {
this.deepProcess(arg);
}
}
}
}
//对敏感数据加密后执行目标方法
Object result = proceedingJoinPoint.proceed();
return result;
}
public void deepProcess(Object obj) throws IllegalAccessException {
if (obj != null) {
//获取对象的所有字段属性并遍历
Field[] declaredFields = obj.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
//判断字段属性上是否标记了@EncryptField注解
if (declaredField.isAnnotationPresent(EncryptField.class)) {
//如果判断结果为真,则取出字段属性值,进行加密、重新赋值
declaredField.setAccessible(true);
Object valObj = declaredField.get(obj);
if (valObj != null) {
String value = valObj.toString();
//开始敏感字段属性值加密
String decrypt;
try {
decrypt = AesSupport.getInstance().encrypt(value);
//把加密后的字段属性值重新赋值
declaredField.set(obj, decrypt);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
}
通过Aop切入拦截标注了NeedEncrypt 、NeedDecrypt的接口函数,获取到参数字段判断是否标注了DecryptField、EncryptField,对标注的进行加解密操作,最后存储到数据库。
在Bean对象类进行字段标注:
public class User{
private String id;
@EncryptField
@DecryptField
private String name;
@EncryptField
@DecryptField
private String phonenum;
}
在接口方法上进行方法标注:
@NeedEncrypt
public int updateUser(User user) throws Exception {
}
@NeedDecrypt
public List<User> queryUser(String userId) throws Exception {
}
注意不要在接口调用路径上进行重复标注,否则会加密两次,最后导致解密不出来。
相关文章:
Springboot 框架中加解密字段后存储数据库
为防止数据库泄露,表里的敏感字段被曝光,需要对用户的重要数据做加密存取。 选择加密算法: 首先,你需要选择适合你的需求的加密算法。一些常见的加密算法包括AES、RSA、SHA等。具体的选择取决于你要加密的数据和安全需求。 引入…...
计算机毕设 大数据工作岗位数据分析与可视化 - python flask
文章目录 0 前言1 课题背景2 实现效果3 项目实现3.1 概括 3.2 Flask实现3.3 HTML页面交互及Jinja2 4 **完整代码**5 最后 0 前言 🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要…...
Maven聚合项目配合Springcloud案例
创建maven项目 导入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache…...
目标检测网络系列——YOLO V1
文章目录 One Stage DectectionYOLO网络正向预测pipline反向传播过程理解grid和grid对应的B个预测框YOLO网络的限制对比实验与其他的real-time detection的对比VOC 2007数据集的错误分析YOLO和Fast RCNN的集成学习VOC 2012数据集结果YOLO模型的泛化性DEMOOne Stage Dectection …...
任务工单发送失败重试方案设计
需求背景: 该系统为一个工单系统,其中任务工单为该系统中的一个模块;任务工单它是需要周期性调度的一种任务类型;可以按照用户配置的时间周期定时性触发的。由于任务需要发送到对应的工作人员上,所以这里需要先对员工进…...
关于 Vue-iClient-MapboxGL 的使用注意事项
官网:https://iclient.supermap.io/web/apis/vue/zh/api/guide/installation.html 关于图的使用,其余的引入步骤不再赘述,仅说注意事项。 推荐使用的是全局引入,也就是完整引入 因为单独引入我踩了不少坑,比如说 cs…...
Go 语言 map 如何顺序读取?
Go 语言中的 map 是一种非常强大的数据结构,它允许我们快速地存储和检索键值对。 然而,当我们遍历 map 时,会有一个有趣的现象,那就是输出的键值对顺序是不确定的。 现象 先看一段代码示例: package mainimport &q…...
flutter StreamSubscription 订阅者 stream
当您使用[Stream.listen]收听[Stream]时 则返回[StreamSubscription]对象 List<StreamSubscription?> subscriptions []; overridevoid initState() {super.initState();//subscriptions列表添加两个StreamSubscription。Stream.listen返回StreamSubscription对象subs…...
安全性算法
目录 一、安全性算法 二、基础术语 三、对称加密与非对称加密 四、数字签名 五、 哈希算法 六、哈希算法碰撞与溢出处理 一、安全性算法 安全性算法的必要性: 安全性算法的必要性是因为在现代数字化社会中,我们经常需要传输、存储和处理敏感的数据…...
解决ASP.NET Core的中间件无法读取Response.Body的问题
概要 本文主要介绍如何在ASP.NET Core的中间件中,读取Response.Body的方法,以便于我们实现更多的定制化开发。本文介绍的方法适用于.Net 3.1 和 .Net 6。 代码和实现 现象解释 首先我们尝试在自定义中间件中直接读取Response.Body,代码如…...
DownloadingImages 下载缓存图片,显示图片文字列表
1. 用到的技术点: 1) Codable : 可编/解码 JSON 数据 2) background threads : 后台线程 3) weak self : 弱引用 4) Combine : 取消器/组合操作 5) Publishers and Subscribers : 发布者与订阅者 6) FileManager : 文件管理器 7) NSCache : 缓存 2. 网址: 2.1 测试接口网址: …...
【应用层协议】HTTPS的加密流程
目录 一、认识HTTPS 二、密文 1、对称加密 2、非对称加密 三、HTTPS加密流程 1、建立连接 2、证书验证 3、密钥协商 4、数据传输 5、关闭连接 总结 在数字化时代,互联网已经成为我们生活和工作中不可或缺的一部分。然而,随着数据的不断增加&a…...
最新AI创作系统/AI绘画系统/ChatGPT系统+H5源码+微信公众号版+支持Prompt应用
一、AI创作系统 SparkAi创作系统是基于国外很火的ChatGPT进行开发的AI智能问答系统和AI绘画系统。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT?小编这里写一个详细图…...
Z410 2023款无人机,专为零基础开发者打造的入门级开源无人机
为什么开发Z410升级款-Easydrone无人机 新手开发者通常在本科阶段加入人工智能行业,对无人机二次开发往往一知半解,面临着C、Python、ROS和mavlink等一系列入门知识,学习起来非常困难,学习的过程中也面临许多挫折。为了帮助零基础…...
elementui修改message消息提示颜色
/* el弹出框样式 */ .el-message {top: 80px !important;border: 0; }.el-message * {color: var(--white) !important;font-weight: 600; }.el-message--success {background: var(--themeBackground); }.el-message--warning {background: var(--gradientBG); }.el-message--…...
Linux和Hadoop的学习
目录 1. Linux的常用快捷键2. Hadoop集群部署问题汇总 1. Linux的常用快捷键 复制:CtrlshiftC 粘贴:CtrlshiftV TAB:补全命令 编写输入:i 退出编写:esc 保存并退出:shift: 2. Hadoop集群部署问…...
通达信指标预警信号,自动发送给微信好友1.0
1.功能介绍:十一节假日期间写了一个,可将股票指标预警信号,自动发送给微信好友/微信群(即电脑端的消息,通过模拟微信操作可在手机上显示)。本工具按通达信写的,如果大智慧,同花顺也能…...
浅谈CDN内容分发与全局负载均衡
CDN简介 CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,…...
【框架风格】解释器模式
1、描述 解释器框架风格(Interpreter Framework Style)是一种软件架构风格,其核心思想是构建一个解释器(Interpreter)来解释并执行特定领域或问题领域的语言或规则。以下是解释器框架风格的一些特点: 1. 领…...
c++视觉图像线性混合
图像线性混合 使用 cv::addWeighted() 函数对两幅图像进行线性混合。alpha 和 beta 是两幅图像的权重,它们之和应该等于1。gamma 是一个可选的增益,这里设置为0。 你可以通过调整 alpha 的值来改变混合比例。如果 alpha0.5,则两幅图像等权重…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
C# winform教程(二)----checkbox
一、作用 提供一个用户选择或者不选的状态,这是一个可以多选的控件。 二、属性 其实功能大差不差,除了特殊的几个外,与button基本相同,所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...
轻量级Docker管理工具Docker Switchboard
简介 什么是 Docker Switchboard ? Docker Switchboard 是一个轻量级的 Web 应用程序,用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器,使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...
goreplay
1.github地址 https://github.com/buger/goreplay 2.简单介绍 GoReplay 是一个开源的网络监控工具,可以记录用户的实时流量并将其用于镜像、负载测试、监控和详细分析。 3.出现背景 随着应用程序的增长,测试它所需的工作量也会呈指数级增长。GoRepl…...
