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

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后台管理系统-数据敏感字段存储加密

前言&#xff1a;     在我们数据库中&#xff0c;存在很多的敏感数据&#xff0c;如用户表中&#xff0c;存在用户电话、身份证号、邮箱等属于用户的敏感信息&#xff0c;我们通常在存入数据库后&#xff0c;将其进行加密存储&#xff0c;以此来保证数据安全性。     …...

熟悉各类游戏设计模式的用途与限制,如 factory、strategy、mvc、object pool 等

良好的系统分析与设计能力要求开发者熟悉并正确运用各种设计模式来解决特定问题。设计模式是一种针对特定问题的通用解决方案&#xff0c;可提高代码的可复用性、可维护性和可扩展性。以下是对一些常见游戏设计模式的详细分析&#xff0c;包括其用途、限制和代码示例。 一、工厂…...

【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 &#xff5c;日常开发中常见问题归纳讲解 前言一、性能问题1.1 数据库访问性能1.2 视图状态&#xff08;在ASP.NET Web Forms 中&#xff09; 二、安全问题2.1 SQL 注入2.2 跨站脚本攻击&#xff08;XSS&#xff09; 三、状态管理问题3.1 会话状态&#xff08;Session …...

【【深入浅出TinyRisc-v】】

深入浅出TinyRisc-v 本代码参考于 https://gitee.com/liangkangnan/tinyriscv 自己理解之后又重新写了一遍 tinyriscv.v // 涓嬮潰鏄鏁翠釜top妯″潡鐨勪功鍐? module tinyriscv(input clk ,input rst_n …...

常见的限流算法

常见的限流算法 限流的定义固定窗口算法滑动窗口算法漏桶算法&#xff08;推荐&#xff09;令牌桶算法(推荐)限流粒度本地限流&#xff08;单机限流&#xff09;分布式限流&#xff08;多机限流&#xff09;分布式限流的实现 限流的定义 限流&#xff0c;也称流量控制。是指系统…...

【Leetcode 每日一题】3159. 查询数组中元素的出现位置

问题背景 给你一个整数数组 n u m s nums nums&#xff0c;一个整数数组 q u e r i e s queries queries 和一个整数 x x x。 对于每个查询 q u e r i e s [ i ] queries[i] queries[i]&#xff0c;你需要找到 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)——事件驱动

【分析】 如果行为树的节点很多&#xff0c;那么会存在要经过很多节点才会走到动作节点的情况。显然&#xff0c;性能上不如状态机。 每帧都需要重新遍历一系列节点才会走到动作节点&#xff0c;而实际上很多条件节点在数帧内不会有变化&#xff0c;这是造成性能问题的重要原…...

3.若依前端项目拉取、部署、访问

因为默认RuoYi-Vue是使用的Vue2,所以需要另外去下载vue3来部署。 拉取代码 git clone https://gitee.com/ys-gitee/RuoYi-Vue3.git 安装node才能执行npm相关的命令 执行命令npm install 如果npm install比较慢的话&#xff0c;需要添加上国内镜像 npm install --registrhttp…...

Debian操作系统相对于Ubuntu有什么优势吗?

更高的稳定性&#xff1a;Debian 以其出色的稳定性闻名&#xff0c;得益于严格的软件包测试和发布流程。其稳定版经过长时间测试与验证&#xff0c;确保了系统的高度稳定&#xff0c;更适合对稳定性要求极高的长期运行服务器环境。而 Ubuntu 虽有稳定版本&#xff0c;但更新周期…...

【漏洞复现】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&#xff0c;打开powershell管理员&#xff0c;输入以下命令,选择1并等待 irm https://get.activated.win | iex参考&#xff1a;https://www.bilibili.com/video/BV1TN411M72J/?sp…...

FFmpeg来从HTTP拉取流并实时推流到RTMP服务器

当使用FFmpeg来从HTTP拉取流并实时推流到RTMP服务器时&#xff0c;你可以使用以下命令&#xff1a; ffmpeg -i http://输入流地址 -c:v copy -c:a copy -f flv rtmp://RTMP服务器地址/应用名称/流名称 这是一个基本的命令示例&#xff0c;其中&#xff1a; - -i http://输入流地…...

Quo Vadis, Anomaly Detection? LLMs and VLMs in the Spotlight 论文阅读

文章信息&#xff1a; 原文链接&#xff1a;https://arxiv.org/abs/2412.18298 Abstract 视频异常检测&#xff08;VAD&#xff09;通过整合大语言模型&#xff08;LLMs&#xff09;和视觉语言模型&#xff08;VLMs&#xff09;取得了显著进展&#xff0c;解决了动态开放世界…...

Rust : tokio中select!

关于tokio的select宏&#xff0c;有不少的用途。包括超时和竞态选择等。 关于select宏需要关注&#xff0c;相关的异步条件&#xff0c;会同时执行&#xff0c;只是当有一个最早完成时&#xff0c;会执行“抛弃”和“对应”策略。 说明&#xff1a;对本文以下素材的来源表示感…...

【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学习记录

开发背景&#xff1a;Window系统主机&#xff0c;在主机上安装了虚拟机&#xff08;VirtualBoxUbuntu23.04&#xff09; 目标实现&#xff1a;在虚拟机&#xff08;Ubuntu&#xff09;中&#xff0c;实现对Maix bit&#xff08;k210&#xff09;开发板的开发 虚拟机的安装参考…...

Wordperss漏洞 DeDeCMS漏洞

Wordperss漏洞 环境搭建 #执⾏命令 cd /vulhub/wordpress/pwnscriptum docker-compose up -d #靶场地址 http://8.155.7.173:8080/wp-admin/ 注册账号 登录 漏洞一&#xff1a;后台修改模板拿WebShell 步骤一&#xff1a;思路是修改其WP的模板写入⼀句话木马后门并访问其文件…...

如何构建有效的AI Agents:从复杂到简约——深度解读Claude实践总结《Building effective agents》(上)

在人工智能技术日新月异的今天&#xff0c;大语言模型(LLM)已经成为技术创新的热点。 然而&#xff0c;在追逐技术前沿的热潮中&#xff0c;我们是否忽视了工程设计的本质&#xff1f; 作为全球人工智能领域的领军企业之一&#xff0c;Anthropic以其在AI安全和伦理方面的深入…...

登高架设作业实操考试需要注意哪些安全细节?

在登高架设作业实操考试中&#xff0c;安全细节是考官重点考察的内容&#xff0c;任何疏忽都可能导致扣分甚至直接判定不合格。以下是必须注意的关键安全细节&#xff0c;按考试流程分类整理&#xff1a; 一、个人防护装备&#xff08;PPE&#xff09;检查与穿戴 安全带 必须…...

深入理解前端DOM:现代Web开发的基石

什么是DOM&#xff1f; DOM&#xff08;Document Object Model&#xff0c;文档对象模型&#xff09;是Web开发中最重要的概念之一。它是一个跨平台、语言独立的接口&#xff0c;将HTML或XML文档表示为树形结构&#xff0c;其中每个节点都是文档的一个部分&#xff08;如元素、…...

工厂方法模式深度解析:从原理到应用实战

作者简介 我是摘星&#xff0c;一名全栈开发者&#xff0c;专注 Java后端开发、AI工程化 与 云计算架构 领域&#xff0c;擅长Python技术栈。热衷于探索前沿技术&#xff0c;包括大模型应用、云原生解决方案及自动化工具开发。日常深耕技术实践&#xff0c;乐于分享实战经验与…...

NIO知识点

一、Java NIO 基础概念 Java NIO&#xff08;New Input/Output&#xff09;是从 Java 1.4 版本开始引入的新的 IO API&#xff0c;它提供了与标准 IO 不同的工作方式。主要特点包括&#xff1a; 面向缓冲区&#xff1a;数据读取到一个稍后处理的缓冲区&#xff0c;需要时可在…...

SpringBoot整合MyBatis完整实践指南

在Java企业级应用开发中&#xff0c;SpringBoot和MyBatis的组合已经成为主流的技术选型方案之一。本文将详细介绍如何从零开始搭建一个基于SpringBoot和MyBatis的项目&#xff0c;包括环境配置、数据库设计、实体类创建、Mapper接口编写以及实际应用等完整流程。 一、环境准备…...

Office文档图片批量导出工具

软件介绍 本文介绍一款专业的Office文档图片批量导出工具。 软件特点 这款软件能够批量导出Word、Excel和PPT中的图片&#xff0c;采用绿色单文件设计&#xff0c;体积小巧仅344KB。 基本操作流程 使用方法十分简单&#xff1a;直接将Word、Excel或PPT文件拖入软件&#xf…...

制造业的未来图景:超自动化与劳动力转型的双重革命

市场现状&#xff1a;传统制造业的转型阵痛 当前全球制造业正站在历史性变革的十字路口。埃森哲对552位工厂经理的全球调研显示&#xff0c;60%的受访者将劳动力转型视为首要战略任务​​&#xff0c;而63%的工厂正在加速部署自动化技术[1]。超过​75%的工厂经理​​认为&…...

LeetCode-链表操作题目

虚拟头指针&#xff0c;在当前head的前面建立一个虚拟头指针&#xff0c;然后哪怕当前的head的val等于提供的val也能进行统一操作 203移除链表元素简单题 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(…...

力扣题解106:从中序与后序遍历序列构造二叉树

一、题目内容 题目要求根据二叉树的中序遍历序列和后序遍历序列来重建二叉树。具体来说&#xff0c;我们需要利用中序遍历序列和后序遍历序列的特点&#xff0c;通过递归的方法逐步构建出完整的二叉树。 中序遍历序列的特点是&#xff1a;左子树 -> 根节点 -> 右子树。后…...

Java开发之定时器学习

面试 一、线程池实现定时器 核心代码&#xff1a; public static void main(String[] args) {ScheduledExecutorService scheduledExecutorService Executors.newScheduledThreadPool(5);Runnable runnable () -> System.out.println("当前线程"Thread.current…...