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 调用 新增 和 更新 批量更新 批量操作 都会触发这里的代码
实现字段加解密

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java智慧工地SaaS管理平台源码:AI/云计算/物联网

智慧工地是指运用信息化手段&#xff0c;围绕施工过程管理&#xff0c;建立互联协同、智能生产、科学管理的施工项目信息化生态圈&#xff0c;并将此数据在虚拟现实环境下与物联网采集到的工程信息进行数据挖掘分析&#xff0c;提供过程趋势预测及专家预案&#xff0c;实现工程…

Elastic Search的RestFul API入门:index索引的增删改查

在我们开始深入探讨Elasticsearch的Restful API之前&#xff0c;有一点非常重要&#xff0c;那就是Elasticsearch存储的数据是JSON结构的。JSON&#xff0c;全称JavaScript Object Notation&#xff0c;是一种轻量级的数据交换格式&#xff0c;易于人阅读和编写&#xff0c;同时…

拜耳阵列(Bayer Pattern)以及常见彩色滤波矩阵(CFA)

一、拜耳阵列的来源 图像传感器将光线转化成电流&#xff0c;光线越亮&#xff0c;电流的数值就越大&#xff1b;光线越暗&#xff0c;电流的数值就越小。图像传感器只能感受光的强弱&#xff0c;无法感受光的波长。由于光的颜色由波长决定&#xff0c;所以图像传播器无法记录…

解决 VS2022 关于 c++17 报错: C2131 表达式必须含有常量值

使用 VS2022 编译 ORB-SLAM3 加载Vocabulary 二进制ORBvoc.bin 时&#xff0c;在 DBOW2 里修改 TemplatedVocabulary.h 代码显示这样的错误&#xff1a; 编译器错误 C2131 表达式的计算结果不是常数 定位到我的代码中&#xff1a; char buf [size_node] ; 原因 &#xff1a; …

Vatee万腾科技创新之舟:Vatee数字化力量引领未来的独特路径

在数字化的大潮中&#xff0c;Vatee万腾如一艘科技创新之舟&#xff0c;在未来的海洋中翱翔。vatee万腾以强大的数字化力量为桨&#xff0c;引领着行业向着新的、独特的路径前行&#xff0c;塑造着数字时代的未来。 Vatee万腾不仅仅是一家科技公司&#xff0c;更是一艘创新之舟…

(八)、基于 LangChain 实现大模型应用程序开发 | 基于知识库的个性化问答 (检索 Retrieval)

检索增强生成&#xff08;RAG&#xff09;的整体工作流程如下&#xff1a; 在构建检索增强生成 (RAG) 系统时&#xff0c;信息检索是核心环节。检索是指根据用户的问题去向量数据库中搜索与问题相关的文档内容&#xff0c;当我们访问和查询向量数据库时可能会运用到如下几种技术…

uni-app:前端实现心跳机制(全局)+局部页面控制心跳暂停和重新心跳

一、App.vue全局中写入心跳 在data中定义变量heartbeatTimer&#xff0c;便于暂停心跳使用在onLaunch中引用开始心跳的方法startHeartbeat()写入开始心跳方法写入暂停心跳方法写入请求后端刷心跳机制 定义变量 // 在全局设置的心跳机制中添加一个变量来保存定时器的标识 data(…

Find My蓝牙耳机|苹果Find My技术与耳机结合,智能防丢,全球定位

蓝牙耳机就是将蓝牙技术应用在免持耳机上&#xff0c;让使用者可以免除恼人电线的牵绊&#xff0c;自在地以各种方式轻松通话。自从蓝牙耳机问世以来&#xff0c;一直是行动商务族提升效率的好工具。正是应为蓝牙耳机小巧无线&#xff0c;人们越来越喜欢随身携带蓝牙耳机出门&a…

【论文阅读】基于隐蔽带宽的汽车控制网络鲁棒认证(一)

文章目录 Abstract第一章 引言1.1 问题陈述1.2 研究假设1.3 贡献1.4 大纲 第二章 背景和相关工作2.1 CAN安全威胁2.1.1 CAN协议设计2.1.2 CAN网络攻击2.1.3 CAN应用攻击 2.2 可信执行2.2.1 软件认证2.2.2 消息身份认证2.2.3 可信执行环境2.2.4 Sancus2.2.5 VulCAN 2.3 侧信道攻…

文件编码、转换、乱码问题

文件编码 用来表示文本内容的字符集和字符编码方式&#xff0c;决定了在文本文件中使用的字符集和字符的二进制表示方式。常见的文件编码包括 UTF-8、UTF-16、ASCII、ISO-8859-1 等。选择文件编码时&#xff0c;需要考虑到所支持的字符集范围、编码方式对特定语言的支持程度以…

手机,蓝牙开发板,TTL/USB模块,电脑四者之间的通讯

一,意图 通过手机蓝牙连接WeMosD1R32开发板,开发板又通过TTL转USB与电脑连接.手机通过蓝牙控制开发板上的LED灯的开,关,闪等动作,在电脑上打开串口监视工具观察其状态.也可以通过电脑上的串口监视工具来控制开发板上LED灯的动作,而在手机蓝牙监测工具中显示灯的状态. 二,原料…

在Go编程中调用外部命令的几种场景

1.摘要 在很多场合, 使用Go语言需要调用外部命令来完成一些特定的任务, 例如: 使用Go语言调用Linux命令来获取执行的结果,又或者调用第三方程序执行来完成额外的任务。在go的标准库中, 专门提供了os/exec包来对调用外部程序提供支持, 本文将对调用外部命令的几种使用方法进行总…

代码逻辑修复与其他爬虫ip库的应用

在一个项目中&#xff0c;由于需要设置 http_proxy 来爬虫IP访问网络&#xff0c;但在使用 requests 库下载文件时遇到了问题。具体表现为在执行 Python 脚本时&#xff0c;程序会阻塞并最终超时&#xff0c;无法正常完成文件下载。 解决方案 针对这个问题&#xff0c;我们可以…

kubenetes-服务发现和负载均衡

一、服务发布 kubenetes把服务发布至集群内部或者外部&#xff0c;服务的三种不同类型&#xff1a; ClusterlPNodePortLoadBalancer ClusterIP是发布至集群内部的一个虚拟IP,通过负载均衡技术转发到不同的pod中。 NodePort解决的是集群外部访问的问题&#xff0c;用户可能不…

基于C#实现最长公共子序列

一、作用 最长公共子序列的问题常用于解决字符串的相似度&#xff0c;是一个非常实用的算法&#xff0c;作为码农&#xff0c;此算法是我们的必备基本功。 二、概念 举个例子&#xff0c;cnblogs 这个字符串中子序列有多少个呢&#xff1f;很显然有 27 个&#xff0c;比如其…

人工智能-深度学习之残差网络(ResNet)

随着我们设计越来越深的网络&#xff0c;深刻理解“新添加的层如何提升神经网络的性能”变得至关重要。更重要的是设计网络的能力&#xff0c;在这种网络中&#xff0c;添加层会使网络更具表现力&#xff0c; 为了取得质的突破&#xff0c;我们需要一些数学基础知识。 ResNet沿…

构建自定义ChatGPT,微软推出Copilot Studio

11月16日&#xff0c;微软在美国西雅图举办“Microsoft Ignite 2023”全球开发者大会。本次人工智能成为重要主题&#xff0c;微软几乎把所有产品都集成了生成式AI功能并发布了一系列全新产品。 其中&#xff0c;微软重磅推出了Copilot Studio&#xff08;预览版&#xff09;&…

助力安全生产--韩施电气为您提供电动机保护及电机故障解决方

上海韩施电气自成立于2008年&#xff0c;是一家专门从事销售电气自动化设备、电力设备、机电设备的综合型贸易公司&#xff0c;公司自成立以来一直专注于EOCR产品的推广销售和技术服务&#xff0c;成为韩国施耐德EOCR在国内的总代理&#xff0c;并授予代理证书&#xff0c;我们…

11 月 11 日 ROS 学习笔记——ROS 架构及概念

文章目录 前言一、 ROS 文件系统级1). 工作空间 Ws2). 功能包3). 消息 msg4). 服务 srv 二、计算图级1). 动态加载节点 nodelet2). 主题 topic3). 服务 srv4). 消息 msg5). 试用练习5). 创建工作空间6). 创建 ROS 功能包和元功能包7). 编译ROS功能包8). 使用 ROS 节点9). 使用主…

球幕投影有哪些常见的物理表现形式?

近年来&#xff0c;投影技术不断发展完善&#xff0c;给内容的表达方式带来了突破&#xff0c;使其展示形式不再局限于平面&#xff0c;即使在弧面、球面等异形幕墙上&#xff0c;也能呈现出令人惊叹的视觉画面。其中球幕投影备受关注&#xff0c;它以半球形屏幕将图像投影到球…