当前位置: 首页 > news >正文

Springboot 对于数据库字段加密方案(此方案是对字符串处理的方案)

背景:在erp开发中,有些用户比较敏感数据库里的数据比较敏感,系统给用户部署后,公司也不想让任何人看到数据,所以就有了数据库字段加密方案。

技术 spring boot + mybatisplus 3.3.1

mybatisplus 实际提供了 字段加密方案
第一 他要钱
第二 他是在实体类上加注解 满足不了我们的需求
在这里插入图片描述

我们的整体需求是
用户可以自定义配置字段
在系统设置里 有个 字段加密 菜单

  1. 点击某个表单 然后显示这个表单所需要的字段
    2.勾选 某个字段 这个字段 就会激活 再提交 更新表单的时候 这个字段就会加密处理
    比如 系统管理 字段加密 他点击了请假表单 然后勾选 请假事由 字段加密
    然后 公司的员工再次提交表单的时候 请假事由 就会被加密 。

这个需求 用户再页面上操作 我们是不需要改代码的 。

如果利用实体类加注解方案 肯定满足不了 因为 每个用户加密的字段不一样,
鬼知道 加密注解要加在哪个实体类上

我们的系统 表单 和表单的字段 都定义在数据库里 所以 可以自由选择 表单和字段
这个根据各自系统自行修改

下面直接分享 加密的代码和思路 。原理我就不再说 ,用到的知识 自行百度即可

第一步 : 首先把用户勾选需要加密的 字段 缓存到redis 减少数据库查询
// 这段代码 就不分享了 自由编写
根据表名 获取 需要加密的字段

 List<String> stringList= BaseDataUtil.getFieldPassword(insertSql.getTableName());

第二步:
引入加密依赖

     <!--对数据库字段进行加密、脱敏--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.3</version></dependency>

第三步:
配置文件 配置
在这里插入图片描述

第四步 编写加密方案 利用的是 框加下的这个类 BaseTypeHandler
对于这个类的介绍 自行百度

自定义一个类 然后继承 这个类 BaseTypeHandler
重写 父类方法

package com.erp.init.handlers;import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.erp.init.utils.BaseDataUtil;
import org.apache.ibatis.logging.jdbc.PreparedStatementLogger;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;import java.lang.reflect.Proxy;
import java.nio.charset.StandardCharsets;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** User: Json* <p>* Date: 2023/11/14**/
public class EncryptHandler extends BaseTypeHandler<String> {/*** 线上运行后勿修改,会影响已加密数据解密*/private static final byte[] KEYS = "shc987654321camp".getBytes(StandardCharsets.UTF_8);private static final String dataWithPrefix = "A:Json";/*** 设置参数*/@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {if (StringUtils.isEmpty(parameter)) {ps.setString(i, null);return;}// 获取动态代理类的 InvocationHandlerPreparedStatementLogger  handler =(PreparedStatementLogger) Proxy.getInvocationHandler(ps);PreparedStatement preparedStatement= handler.getPreparedStatement();MetaObject stmtMetaObj = SystemMetaObject.forObject(preparedStatement);String sql =  stmtMetaObj.getValue("sql").toString();//System.out.println("sql:"+sql);SqlResult updateSql= updateSql(sql);if(!ObjectUtils.isEmpty(updateSql)){if(updateSql.getColumnNames().size()>(i-1)){//            System.out.println("更新数据:"+updateSql);
//            System.out.println(i+"===>"+updateSql.getColumnNames().get(i-1));List<String> stringList= BaseDataUtil.getFieldPassword(updateSql.getTableName());if (!CollectionUtils.isEmpty(stringList) &&!CollectionUtils.isEmpty(updateSql.getColumnNames()) &&stringList.contains(updateSql.getColumnNames().get(i-1))) {AES aes = SecureUtil.aes(KEYS);String encrypt = aes.encryptHex(parameter);ps.setString(i,dataWithPrefix+ encrypt);} else {ps.setString(i, parameter);}}else{ps.setString(i, parameter);}}SqlResult insertSql=insetSQl(sql);if(!ObjectUtils.isEmpty(insertSql)){if(insertSql.getColumnNames().size()>(i-1)){//            System.out.println("新增数据:"+insertSql);
//            System.out.println(i+"===>"+insertSql.getColumnNames().get(i-1));List<String> stringList= BaseDataUtil.getFieldPassword(insertSql.getTableName());if (!CollectionUtils.isEmpty(stringList) &&!CollectionUtils.isEmpty(insertSql.getColumnNames()) &&stringList.contains(insertSql.getColumnNames().get(i-1))) {AES aes = SecureUtil.aes(KEYS);String encrypt = aes.encryptHex(parameter);ps.setString(i, dataWithPrefix+ encrypt);} else {ps.setString(i, parameter);}}else{ps.setString(i, parameter);}}if(ObjectUtils.isEmpty(insertSql) && ObjectUtils.isEmpty(updateSql) ){ps.setString(i, parameter);}}/*** 获取值*/@Overridepublic String getNullableResult(ResultSet rs, String columnName) throws SQLException {return decrypt(rs.getString(columnName),columnName,rs.getMetaData().getTableName(1));}/*** 获取值*/@Overridepublic String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {//  return null;return rs.getString(columnIndex);}/*** 获取值*/@Overridepublic String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {// return null;return cs.getString(columnIndex);}public String decrypt(String value,String columnName,String tableName) {// return  value;if (null == value) {return null;}List<String> stringList= BaseDataUtil.getFieldPassword(tableName);if(CollectionUtils.isEmpty(stringList)){return  value;}if (value.startsWith(dataWithPrefix) && stringList.contains(columnName)) {// 是新数据,去掉前缀String decryptedData = value.substring(dataWithPrefix.length());return SecureUtil.aes(KEYS).decryptStr(decryptedData);} else {return  value;}}public static SqlResult  updateSql(String sql) {// 假设你已经有了sql字符串// String sql = "UPDATE your_table SET column1 = value1, column2 = value2 WHERE condition";// 匹配UPDATE语句Pattern updatePattern = Pattern.compile("UPDATE\\s+([^\\s]+)\\s+SET\\s+([^\\s]+\\s*=\\s*[^,]+(,\\s*[^\\s]+\\s*=\\s*[^,]+)*)\\s+WHERE\\s+(.+)");Matcher updateMatcher = updatePattern.matcher(sql);// 如果是UPDATE语句if (updateMatcher.matches()) {String tableName = updateMatcher.group(1); // 获取表名String setClause = updateMatcher.group(2); // 获取SET子句// 提取更新的字段名List<String> columnNames = extractColumnNames(setClause);// 返回表名和字段名的信息return new SqlResult(tableName, columnNames);}return null;}private static List<String> extractColumnNames(String setClause) {List<String> columnNames = new ArrayList<>();String[] columns = setClause.split("\\s*,\\s*");for (String column : columns) {String[] parts = column.split("\\s*=\\s*");String columnName = parts[0].trim();columnNames.add(columnName);}return columnNames;}private static List<String> extractColumnNamesInsert(String columnsClause) {String[] columns = columnsClause.split("\\s*,\\s*");List<String> columnNames = new ArrayList<>();for (String column : columns) {columnNames.add(column.trim());}return columnNames;}public static  SqlResult insetSQl(String sql) {// 假设你已经有了sql字符串// String sql = "INSERT INTO your_table (column1, column2) VALUES (value1, value2)";// 匹配INSERT语句Pattern insertPattern = Pattern.compile("INSERT\\s+INTO\\s+([^\\s]+)\\s*\\(([^)]+)\\)\\s*VALUES\\s*\\(([^)]+)\\)");Matcher insertMatcher = insertPattern.matcher(sql);// 如果是INSERT语句if (insertMatcher.matches()) {String tableName = insertMatcher.group(1); // 获取表名String columnsClause = insertMatcher.group(2); // 获取列名的部分//  String valuesClause = insertMatcher.group(3); // 获取值的部分// 提取新增的字段名和对应的值List<String> columnNames = extractColumnNamesInsert(columnsClause);// 返回表名、字段名和对应值的信息return new SqlResult(tableName, columnNames);}return null;}}

第五步:
把这个类注册到配置文件中

package com.erp.init.mybatisplus;import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.erp.init.handlers.EncryptHandler;
import org.apache.ibatis.type.JdbcType;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** User: Json* <p>* Date: 2023/11/15**/
@Configuration
public class MyBatisConfig {@Beanpublic ConfigurationCustomizer configurationCustomizer() {return configuration -> {//String.class, JdbcType.VARCHAR  这个是 只对 字符串进行处理// int float 的加密 根据字符串再扩展一个类就行了 configuration.getTypeHandlerRegistry().register(String.class, JdbcType.VARCHAR, new EncryptHandler());// 注册其他类型处理器};}
}

这要编写后 每次 mybatisplus 调用 新增 和 更新 批量更新 批量操作 都会触发这里的代码
实现字段加解密

相关文章:

Springboot 对于数据库字段加密方案(此方案是对字符串处理的方案)

背景:在erp开发中&#xff0c;有些用户比较敏感数据库里的数据比较敏感&#xff0c;系统给用户部署后&#xff0c;公司也不想让任何人看到数据&#xff0c;所以就有了数据库字段加密方案。 技术 spring boot mybatisplus 3.3.1 mybatisplus 实际提供了 字段加密方案 第一 他…...

[C++]:8.C++ STL引入+string(介绍)

C STL引入string(介绍&#xff09; 一.STL引入&#xff1a;1.什么是STL2.什么是STL的版本&#xff1a;2-1&#xff1a;原始版本&#xff1a;2-2&#xff1a;P. J 版本&#xff1a;2-3&#xff1a;RW 版本&#xff1a;2-4&#xff1a;SGL版本&#xff1a; 3.STL 的六大组件&…...

C++基础从0到1入门编程(三)

系统学习C 方便自己日后复习&#xff0c;错误的地方希望积极指正 往期文章&#xff1a; C基础从0到1入门编程&#xff08;一&#xff09; C基础从0到1入门编程&#xff08;二&#xff09; 参考视频&#xff1a; 1.黑马程序员匠心之作|C教程从0到1入门编程,学习编程不再难 2.系统…...

[Jenkins] 物理机 安装 Jenkins

这里介绍Linux CentOS系统直接Yum 安装 Jenkins&#xff0c;不同系统之间类似&#xff0c;操作命令差异&#xff0c;如&#xff1a;Ubuntu用apt&#xff1b; 0、安装 Jenkins Jenkins是一个基于Java语言开发的持续构建工具平台&#xff0c;主要用于持续、自动的构建/测试你的软…...

设计模式 -- 适配器模式(Adapter Pattern)

适配器模式&#xff1a;属于结构型模式&#xff0c;结合了两个独立接口的功能&#xff0c;作为 两个不兼容的接口之间的桥梁 。 介绍 意图&#xff1a;将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。主要…...

Axios传值的几种方式

<body><script src"https://unpkg.com/axios/dist/axios.min.js"></script></body> axios基本使用 默认是get请求 注意&#xff1a;get请求无请求体&#xff0c;可以有body,但是不建议带 使用get方式进行无参请求 <script>axios(…...

git pull 报错 error object file is empty , The remote end hung up unexpectedly

报错原因分析&#xff1a;git pull的时候服务器在重启&#xff0c;导致git文件损坏 方法来源&#xff1a; 解决git错误: error object file is empty , The remote end hung up unexpectedly-CSDN博客 亲测有效 find .git/objects/ -type f -empty | xargs rm git fetch -p…...

手机数码类展示预约小程序效果如何

对于一家手机数码/电脑品牌来说&#xff0c;研发产品或衍生产品不少&#xff0c;通常会通过线上商城进行售卖。十年以来&#xff0c;流量成本逐渐增加&#xff0c;获客不易也难以寻找到合适的渠道&#xff0c;即使通过广告形式也因缺乏创意而耗时耗力&#xff0c;效果不佳。 同…...

图神经网络:消息传递算法

一、说明 图网络-GNN&#xff08;Graph Neural Networks&#xff09;是近几年研究的主题之一&#xff0c;虽不及深度神经网络那么火爆&#xff0c;但在一些领域&#xff0c;如分子化学方面是不得不依赖的理论。本文就一些典型意义的图神经网络消息传递展开阐述。 二、图网络简述…...

安全+Linux!IBM新一代大型机Z14全新发布

导读本周&#xff0c;以“架构 人机同行”为主题的IBM Systems创行者高峰论坛在北京召开&#xff0c;IBM全球及大中华区硬件系统部负责人&#xff0c;金融、医疗、制造等领域的企业、合作伙伴共与这一年度盛会&#xff0c;探讨认知时代下的基础架构技术趋势及IBM硬件系统业务的…...

Java中的局部变量和成员变量的区别

局部变量和成员变量的区别 区别1&#xff1a;代码中位置不同 成员变量&#xff1a;类中方法外定义的变量 局部变量&#xff1a;方法中定义的变量 代码块中定义的变量 区别2&#xff1a;代码的作用范围 成员变量&#xff1a;当前类的很多方法 局部变量&#xff1a;当前一…...

基于C++实现循环赛日程表(分治算法)

一、问题描叙 设有n2^k个运动员&#xff0c;要进行网球循环赛。现在要设计一个满足以下要求的比赛日程表 每个选手必须与其他n-1个选手各赛一场每个选手一天只能赛一次循环赛一共进行n-1天 二、问题分析 按此要求可将比赛日程表设计成n行n-1列的表&#xff0c;在表中第 i 行…...

基于uni-app的汽车租赁app的设计与实现

1.项目背景及意义 项目背景&#xff1a; 随着人们生活水平的提高&#xff0c;汽车租赁服务在城市中变得越来越普及。传统的租车方式存在一些问题&#xff0c;比如租车流程繁琐、费用不透明、选择有限等。因此&#xff0c;开发一款基于uni-app的汽车租赁app成为了满足用户需求…...

3.8-镜像的发布

如果我们想将image push到docker hub里面&#xff0c;那么我们的image的名字一定要是这种格式&#xff1a;docker hub id/imageName&#xff0c;例如&#xff1a;lvdapiaoliang/hello-docker docker hub个人账户设置地址&#xff1a; 在push之前要先登录&#xff1a; docker l…...

Navicat 基于 GaussDB 主备版的快速入门

Navicat Premium&#xff08;16.2.8 Windows版或以上&#xff09; 已支持对GaussDB 主备版的管理和开发功能。它不仅具备轻松、便捷的可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结构同步、协同合作、数据迁移等&#xff09;&#xff0c;这…...

String的字符串拼接

java中 String a “123” “234”; String b “123”; String c b “234”; 其中a和c的区别是什么&#xff1f; a c 为什么为false 在Java中&#xff0c;字符串的处理特别是涉及到字符串常量和字符串变量的连接时&#xff0c;会涉及到字符串池&#xff08;String Pool&a…...

反渗透水处理成套设备有哪些

反渗透水处理成套设备主要包括反渗透装置、预处理系统、控制系统等部分。 反渗透装置&#xff1a;反渗透水处理设备的核心部分&#xff0c;由反渗透膜、压力容器、膜组件等组成。反渗透膜是一种高分子材料制成的半透膜&#xff0c;能够截留水中的溶解盐、有机物、细菌等杂质&a…...

DPC15 国产带有 SPI 接口的独立 CAN 控制器兼容替代MCP2551

DPC15是一款独立控制器局域网络&#xff08;Controller Area Network&#xff0c;CAN&#xff09;协议控制器&#xff0c;完全支持CAN V2.0B技术规范。该器件能发送和接收标准和扩展数据帧以及远程帧。 DPC15自带的两个验收屏蔽寄存器和六个验收滤波寄存器可以过滤掉不想要的报…...

【ELK01】ELK简介以及ElasticSearch安装、ES客户端工具-Head安装、报错问题整理

有一段时间没有更新这个专栏了,最近在用ELK相关的技术,今天开始写一下ELK的系列的内容,与大家共同学习 一、什么是ELK ELK 是elastic公司提供的一套完整的日志收集以及展示的解决方案,是三个产品的首字母缩写,分别是ElasticSearch、Logstash 和 Kibana。 1. E-ELASTICS…...

根据音频绘制频谱

根据音频链接绘制频谱图 封装 // 可以这样使用 也可以 import { AudioContext } from standardized-audio-context; const getAudioContext window.AudioContext ||window.webkitAudioContext ||window.mozAudioContext ||window.msAudioContext;const clearArr []export c…...

增程式电动汽车预测型能量管理策略【附算法】

✨ 长期致力于增程式电动汽车、能量管理策略、车速预测、广义回归神经网络、动态规划研究工作&#xff0c;擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流&#xff0c;点击《获取方式》 &#xff08;1&#xff09;基于AIC准则的自…...

如何快速清理Windows右键菜单:终极管理工具完整指南

如何快速清理Windows右键菜单&#xff1a;终极管理工具完整指南 【免费下载链接】ContextMenuManager &#x1f5b1;️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 你是不是也遇到过这样的烦恼&#xff1f;安装的软…...

AI资讯简报如何成为工程师的决策加速器

1. 项目概述&#xff1a;一份真正“够用”的AI资讯简报&#xff0c;到底长什么样&#xff1f;“This AI newsletter is all you need #35”——光看标题&#xff0c;你可能以为这是某份泛泛而谈的行业 roundup&#xff0c;或是又一个堆砌链接、靠标题党吸睛的邮件列表。但在我连…...

Python数据分析入门 - BV1xX4y1Z7Y8

Python数据分析入门 - BV1xX4y1Z7Y8 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱&#xff0c;支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools 本视频主要介绍Python数据分析的…...

华硕笔记本性能控制终极指南:用GHelper告别臃肿,拥抱高效

华硕笔记本性能控制终极指南&#xff1a;用GHelper告别臃肿&#xff0c;拥抱高效 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivoboo…...

装配骨架:每一帧重新构建简笔人物,文本围绕当前姿势环绕显示

【导语&#xff1a;资讯介绍了装配骨架的相关情况&#xff0c;包括每一帧重新构建简笔人物&#xff0c;文本围绕当前姿势环绕显示&#xff0c;还有波浪动画等视觉效果及闲置状态。】简笔人物的帧构建在装配骨架的过程中&#xff0c;每一帧都会依据基本的排除部分重新构建一个简…...

构建企业级AI对话平台:Open WebUI部署架构深度解析

构建企业级AI对话平台&#xff1a;Open WebUI部署架构深度解析 【免费下载链接】open-webui User-friendly AI Interface (Supports Ollama, OpenAI API, ...) 项目地址: https://gitcode.com/GitHub_Trending/op/open-webui 在AI技术快速发展的今天&#xff0c;如何构建…...

ESP-IDF+vscode开发ESP32第十三讲——NVS

目录 一、NVS梳理 1.1 分区 (Partition)&#xff1a;NVS 的专属“仓库” 1.2 页面 (Page)&#xff1a;仓库里的“货架” 1.3 条目 (Entry)&#xff1a;货架上的“最小存储格” 1.4 键值对 (Key-Value Pair)&#xff1a;实际存放的“货物” 1.5 命名空间 (Namespace)&…...

别再硬扛了!书匠策AI把毕业论文拆成了“填空题“,2025届必看科普

各位被毕业论文逼到怀疑人生的朋友们&#xff0c;今天这期内容&#xff0c;我想用一种你从没听过的方式&#xff0c;给你拆解一个工具——书匠策AI&#xff08; 官网直达&#xff1a;www.shujiangce.com微信搜一搜"书匠策AI"可关注公众号&#xff09;。 先抛一个扎心…...

GitHub加速终极指南:5分钟告别访问卡顿,让开发效率翻倍

GitHub加速终极指南&#xff1a;5分钟告别访问卡顿&#xff0c;让开发效率翻倍 【免费下载链接】FastGithub github定制版的dns服务&#xff0c;解析访问github最快的ip 项目地址: https://gitcode.com/gh_mirrors/fa/FastGithub 你是否经历过这样的场景&#xff1a;在关…...