MONI后台管理系统-数据敏感字段存储加密
前言:
在我们数据库中,存在很多的敏感数据,如用户表中,存在用户电话、身份证号、邮箱等属于用户的敏感信息,我们通常在存入数据库后,将其进行加密存储,以此来保证数据安全性。
加密的方式有很多,如摘要加密、对称加密、非对称加密等,这里不做描述。因为我们的数据加密存储后,查询出来还需解密进行显示,我们使用对称加密的方式来实现加密。
常用的对称加密有DES、AES、3DES以及国密算法SM4等。前者我们比较推荐使用AES,那么我们这次的对称加密实现将使用Aes以及SM4。
而对于代码来说,我们在存储时需要加密,查询时又需要解密,如果每个方法我们都去这样操作,冗余代码会非常的多,是非常不合适的。因为我们集成的是Mybatis-plus框架,我们可以自定义TypeHandler字段类型处理器来实现我们这一需求。类型处理器(TypeHandler)扮演着 JavaType 与 JdbcType 之间转换的桥梁角色。它们用于在执行 SQL 语句时,将 Java 对象的值设置到 PreparedStatement 中,或者从 ResultSet 或 CallableStatement 中取出值。在类型处理器中,我们针对特定字段集成加解密的逻辑即可。
本项目代码已上传至gitee Omni-Admin
1. 定义EncryptHandler字段类型处理器。
package com.omni.admin.mybatis;import com.omni.admin.context.SpringContext;
import com.omni.admin.encryption.EncryptionTypeEnum;
import com.omni.admin.encryption.symmetricalencryption.SymmetricalEncryption;
import com.omni.admin.encryption.symmetricalencryption.SymmetricalEncryptionFactory;
import com.omni.admin.properties.EncryptionProperties;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;/*** 数据加解密拦截*/
public class EncryptHandler extends BaseTypeHandler<String> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {ps.setString(i, encrypt(parameter));}@Overridepublic String getNullableResult(ResultSet rs, String columnName) throws SQLException {return decrypt(rs.getString(columnName));}@Overridepublic String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return decrypt(rs.getString(columnIndex));}@Overridepublic String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return decrypt(cs.getString(columnIndex));}/*** 加密* */private String encrypt(String params){EncryptionProperties encryptionProperties = SpringContext.getBean(EncryptionProperties.class);Boolean enableStorage = encryptionProperties.getEnableStorage();if (Boolean.FALSE.equals(enableStorage)) return params;SymmetricalEncryption symmetricalEncryption = getSymmetricalEncryption(encryptionProperties.getStorageEncryption());return symmetricalEncryption.encrypt(params);}/*** 解密*/private String decrypt(String params){EncryptionProperties encryptionProperties = SpringContext.getBean(EncryptionProperties.class);Boolean enableStorage = encryptionProperties.getEnableStorage();if (Boolean.FALSE.equals(enableStorage)) return params;SymmetricalEncryption symmetricalEncryption = getSymmetricalEncryption(encryptionProperties.getStorageEncryption());return symmetricalEncryption.decrypt(params);}private SymmetricalEncryption getSymmetricalEncryption(EncryptionTypeEnum encryptionTypeEnum){return SymmetricalEncryptionFactory.getSymmetricalEncryption(encryptionTypeEnum);}
}
2. 使用该处理器
我们以用户实体为例,当中有电话、身份证号和你邮箱。在@TableField注解中,加入EncryptHandler类型处理器即可。另外还需在@TableName注解中,添加autoResultMap = true属性,这样在查询时才能进行解密。
@EqualsAndHashCode(callSuper = true)
@Data
@TableName(value = "omni_user_info",autoResultMap = true)
public class UserInfo extends BaseEntity {/*** 用户电话*/@Sensitize(rule = SensitizeRuleEnums.MOBILE_PHONE)@TableField(value = "F_PHONE",typeHandler = EncryptHandler.class)private String phone;/*** 用户邮箱*/@TableField(value = "F_EMAIL",typeHandler = EncryptHandler.class)private String email;/*** 用户身份证*/@TableField(value = "F_ID_CARD",typeHandler = EncryptHandler.class)private String idCard;}
3. 查看效果
我们可在看到数据库中目前存储的就是密文了。

4. 加密的实现
这里我们考虑到设计以及后续的拓展性,我们对称加密的实现将会有多个,使用工厂模式来设计编码。
4.1 创建对称机密接口类SymmetricalEncryption
该接口空中提供了加密接口和解密接口,由子类去实现。
package com.omni.admin.encryption.symmetricalencryption;public interface SymmetricalEncryption {/*** 加密* @param plaintext 明文* @param key 密钥* @return 加密后的值*/String encrypt(String plaintext, String key);/*** 加密* @param plaintext 明文* @return 加密后的值*/String encrypt(String plaintext);/*** 解密* @param ciphertext 密文* @param key 密钥* @return 解密后的明文*/String decrypt(String ciphertext, String key);/*** 解密* @param ciphertext 密文* @return 解密后的明文*/String decrypt(String ciphertext);
}
4.2 AES加解密实现SymmetricalEncryptionAes类
maven依赖
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.70</version></dependency>
4.3 加解密工具类EncryptUtils
package com.omni.admin.utils;import com.omni.admin.exception.ExceptionStatus;
import com.omni.admin.exception.OmniException;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;public class EncryptUtils {static{Security.addProvider(new BouncyCastleProvider());}private static final Logger log = LoggerFactory.getLogger(EncryptUtils.class);public static String sm4Encrypt(String plainString, String key) {return encrypt(plainString,key,"SM4");}public static String aesEncrypt(String plainString, String key) {return encrypt(plainString,key,"AES");}public static String sm4Decrypt(String plainString, String key) {return decrypt(plainString,key,"SM4");}public static String aesDecrypt(String plainString, String key) {return decrypt(plainString,key,"AES");}private static String encrypt(String plainString, String key,String algorithm) {try {// 创建密钥规范SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), algorithm);// 获取Cipher对象实例(BC中SM4默认使用ECB模式和PKCS5Padding填充方式,因此下列模式和填充方式无需指定)Cipher cipher = Cipher.getInstance(algorithm + "/ECB/PKCS5Padding");// 初始化Cipher为加密模式cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);// 获取加密byte数组byte[] cipherBytes = cipher.doFinal(plainString.getBytes(StandardCharsets.UTF_8));// 输出为Base64编码return Base64.encodeBase64String(cipherBytes);} catch (Exception e) {log.error(e.getMessage(), e);throw new OmniException(ExceptionStatus.ENCRYPT_ERROR);}}private static String decrypt(String ciphertext, String key,String algorithm) {try {// 创建密钥规范SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), algorithm);// 获取Cipher对象实例(Java8中AES默认使用ECB模式和PKCS5Padding填充方式,因此下列模式和填充方式无需指定)Cipher cipher = Cipher.getInstance(String.format("%s/ECB/PKCS5Padding", algorithm));// 初始化Cipher为解密模式cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);// 获取加密byte数组byte[] cipherBytes = cipher.doFinal(Base64.decodeBase64(ciphertext));// 输出为字符串return new String(cipherBytes);} catch (Exception e) {log.error(e.getMessage(), e);throw new OmniException(ExceptionStatus.ENCRYPT_ERROR);}}
}
package com.omni.admin.encryption.symmetricalencryption;
import com.omni.admin.utils.EncryptUtils;public class SymmetricalEncryptionAes implements SymmetricalEncryption {public static final String KEY = "cf410532f92a47de";@Overridepublic String encrypt(String plaintext, String key) {return EncryptUtils.aesEncrypt(plaintext,key);}@Overridepublic String encrypt(String plaintext) {return encrypt(plaintext, KEY);}@Overridepublic String decrypt(String ciphertext, String key) {return EncryptUtils.aesDecrypt(ciphertext,key);}@Overridepublic String decrypt(String ciphertext) {return decrypt(ciphertext, KEY);}
}
4.4 国密算法SymmetricalEncryptionSM4类
package com.omni.admin.encryption.symmetricalencryption;import com.omni.admin.utils.EncryptUtils;public class SymmetricalEncryptionSM4 implements SymmetricalEncryption {public static final String KEY = "cf410532f92a47de";@Overridepublic String encrypt(String plaintext, String key) {return EncryptUtils.sm4Encrypt(plaintext,key);}@Overridepublic String encrypt(String plaintext) {return encrypt(plaintext, KEY);}@Overridepublic String decrypt(String ciphertext, String key) {return EncryptUtils.sm4Decrypt(ciphertext,key);}@Overridepublic String decrypt(String ciphertext) {return decrypt(ciphertext, KEY);}
}
4.5 加解密工厂类SymmetricalEncryptionFactory
package com.omni.admin.encryption.symmetricalencryption;import com.omni.admin.encryption.EncryptionTypeEnum;
import com.omni.admin.exception.ExceptionStatus;
import com.omni.admin.exception.OmniException;public class SymmetricalEncryptionFactory {private SymmetricalEncryptionFactory() {}public static SymmetricalEncryption getSymmetricalEncryption(EncryptionTypeEnum encryptionType) {if (EncryptionTypeEnum.AES.equals(encryptionType)){return new SymmetricalEncryptionAes();}if (EncryptionTypeEnum.SM4.equals(encryptionType)){return new SymmetricalEncryptionSM4();}throw new OmniException(ExceptionStatus.NOT_FOUND_SIGNATURE);}
}
4.6 枚举类
package com.omni.admin.encryption;import lombok.Getter;@Getter
public enum EncryptionTypeEnum {MD5("md5"),SHA256("sha256"),SM3("sm3"),AES("AES"),SM4("SM4");private final String type;EncryptionTypeEnum(String type) {this.type = type;}
}
5. 加解密类型的配置化EncryptionProperties
package com.omni.admin.properties;import com.omni.admin.encryption.EncryptionTypeEnum;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;@Data
@Configuration
@ConfigurationProperties(prefix = "omni.encryption")
public class EncryptionProperties {/*** 是否开启数据签名*/private Boolean enableSign = true;/*** 是否开启存储加密*/private Boolean enableStorage = true;/*** 签名加密算法*/private EncryptionTypeEnum signAlgorithm = EncryptionTypeEnum.SM3;/*** 存储加密算法*/private EncryptionTypeEnum storageEncryption = EncryptionTypeEnum.SM4;
}相关文章:
MONI后台管理系统-数据敏感字段存储加密
前言: 在我们数据库中,存在很多的敏感数据,如用户表中,存在用户电话、身份证号、邮箱等属于用户的敏感信息,我们通常在存入数据库后,将其进行加密存储,以此来保证数据安全性。 …...
熟悉各类游戏设计模式的用途与限制,如 factory、strategy、mvc、object pool 等
良好的系统分析与设计能力要求开发者熟悉并正确运用各种设计模式来解决特定问题。设计模式是一种针对特定问题的通用解决方案,可提高代码的可复用性、可维护性和可扩展性。以下是对一些常见游戏设计模式的详细分析,包括其用途、限制和代码示例。 一、工厂…...
【RabbitMQ高级篇】消息可靠性问题(1)
目录 1.消息可靠性 1.1.生产者消息确认 1.1.1.修改配置 1.1.2.定义Return回调 1.1.3.定义ConfirmCallback 1.2.消息持久化 1.2.1.交换机持久化 1.2.2.队列持久化 1.2.3.消息持久化 1.3.消费者消息确认 1.3.1.演示none模式 1.3.2.演示auto模式 1.4.消费失败重试机制…...
ASP.NET |日常开发中常见问题归纳讲解
ASP.NET |日常开发中常见问题归纳讲解 前言一、性能问题1.1 数据库访问性能1.2 视图状态(在ASP.NET Web Forms 中) 二、安全问题2.1 SQL 注入2.2 跨站脚本攻击(XSS) 三、状态管理问题3.1 会话状态(Session …...
【【深入浅出TinyRisc-v】】
深入浅出TinyRisc-v 本代码参考于 https://gitee.com/liangkangnan/tinyriscv 自己理解之后又重新写了一遍 tinyriscv.v // 涓嬮潰鏄鏁翠釜top妯″潡鐨勪功鍐? module tinyriscv(input clk ,input rst_n …...
常见的限流算法
常见的限流算法 限流的定义固定窗口算法滑动窗口算法漏桶算法(推荐)令牌桶算法(推荐)限流粒度本地限流(单机限流)分布式限流(多机限流)分布式限流的实现 限流的定义 限流,也称流量控制。是指系统…...
【Leetcode 每日一题】3159. 查询数组中元素的出现位置
问题背景 给你一个整数数组 n u m s nums nums,一个整数数组 q u e r i e s queries queries 和一个整数 x x x。 对于每个查询 q u e r i e s [ i ] queries[i] queries[i],你需要找到 n u m s nums nums 中第 q u e r i e s [ i ] queries[i] q…...
xadmin后台首页增加一个导入数据按钮
xadmin后台首页增加一个导入数据按钮 效果 流程 1、在添加小组件中添加一个html页面 2、写入html代码 3、在urls.py添加导入数据路由 4、在views.py中添加响应函数html代码 <!DOCTYPE html> <html lang...
行为树详解(5)——事件驱动
【分析】 如果行为树的节点很多,那么会存在要经过很多节点才会走到动作节点的情况。显然,性能上不如状态机。 每帧都需要重新遍历一系列节点才会走到动作节点,而实际上很多条件节点在数帧内不会有变化,这是造成性能问题的重要原…...
3.若依前端项目拉取、部署、访问
因为默认RuoYi-Vue是使用的Vue2,所以需要另外去下载vue3来部署。 拉取代码 git clone https://gitee.com/ys-gitee/RuoYi-Vue3.git 安装node才能执行npm相关的命令 执行命令npm install 如果npm install比较慢的话,需要添加上国内镜像 npm install --registrhttp…...
Debian操作系统相对于Ubuntu有什么优势吗?
更高的稳定性:Debian 以其出色的稳定性闻名,得益于严格的软件包测试和发布流程。其稳定版经过长时间测试与验证,确保了系统的高度稳定,更适合对稳定性要求极高的长期运行服务器环境。而 Ubuntu 虽有稳定版本,但更新周期…...
【漏洞复现】CVE-2015-3337 Arbitrary File Reading
漏洞信息 NVD - CVE-2015-3337 Directory traversal vulnerability in Elasticsearch before 1.4.5 and 1.5.x before 1.5.2, when a site plugin is enabled, allows remote attackers to read arbitrary files via unspecified vectors. 在安装了具有“site”功能的插件以…...
win10、win11-鼠标右键还原、暂停更新
系统优化 win 10jihuo win 11jihuo鼠标右键还原暂停更新 update 2024.12.28win 10 jihuo winx,打开powershell管理员,输入以下命令,选择1并等待 irm https://get.activated.win | iex参考:https://www.bilibili.com/video/BV1TN411M72J/?sp…...
FFmpeg来从HTTP拉取流并实时推流到RTMP服务器
当使用FFmpeg来从HTTP拉取流并实时推流到RTMP服务器时,你可以使用以下命令: ffmpeg -i http://输入流地址 -c:v copy -c:a copy -f flv rtmp://RTMP服务器地址/应用名称/流名称 这是一个基本的命令示例,其中: - -i http://输入流地…...
Quo Vadis, Anomaly Detection? LLMs and VLMs in the Spotlight 论文阅读
文章信息: 原文链接:https://arxiv.org/abs/2412.18298 Abstract 视频异常检测(VAD)通过整合大语言模型(LLMs)和视觉语言模型(VLMs)取得了显著进展,解决了动态开放世界…...
Rust : tokio中select!
关于tokio的select宏,有不少的用途。包括超时和竞态选择等。 关于select宏需要关注,相关的异步条件,会同时执行,只是当有一个最早完成时,会执行“抛弃”和“对应”策略。 说明:对本文以下素材的来源表示感…...
【hackmyvm】hacked靶机wp
tags: HMVrootkitDiamorphine Type: wp 1. 基本信息^toc 文章目录 1. 基本信息^toc2. 信息收集2.1. 端口扫描2.2. 目录扫描2.3. 获取参数 3. 提权 靶机链接 https://hackmyvm.eu/machines/machine.php?vmHacked 作者 sml 难度 ⭐️⭐️⭐️⭐️️ 2. 信息收集 2.1. 端口扫描…...
MaixBit k210学习记录
开发背景:Window系统主机,在主机上安装了虚拟机(VirtualBoxUbuntu23.04) 目标实现:在虚拟机(Ubuntu)中,实现对Maix bit(k210)开发板的开发 虚拟机的安装参考…...
Wordperss漏洞 DeDeCMS漏洞
Wordperss漏洞 环境搭建 #执⾏命令 cd /vulhub/wordpress/pwnscriptum docker-compose up -d #靶场地址 http://8.155.7.173:8080/wp-admin/ 注册账号 登录 漏洞一:后台修改模板拿WebShell 步骤一:思路是修改其WP的模板写入⼀句话木马后门并访问其文件…...
如何构建有效的AI Agents:从复杂到简约——深度解读Claude实践总结《Building effective agents》(上)
在人工智能技术日新月异的今天,大语言模型(LLM)已经成为技术创新的热点。 然而,在追逐技术前沿的热潮中,我们是否忽视了工程设计的本质? 作为全球人工智能领域的领军企业之一,Anthropic以其在AI安全和伦理方面的深入…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
手机平板能效生态设计指令EU 2023/1670标准解读
手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
