当前位置: 首页 > 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…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...

路由基础-路由表

本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中&#xff0c;往往存在多个不同的IP网段&#xff0c;数据在不同的IP网段之间交互是需要借助三层设备的&#xff0c;这些设备具备路由能力&#xff0c;能够实现数据的跨网段转发。 路由是数据通信网络中最基…...