数据加密的两种方案

说明:本文介绍对项目中的数据加密的两种方案;

场景

源自真实的项目需求,需要我们对系统中的敏感数据(如手机号、证件号等)进行加密处理,即存入到数据库中的是加密后的密文数据。加密本身是不难的,选定一款算法,写个工具类,写个加密、解密方法,密钥存代码里就可以。历史数据,就写个一次性代码,读取、加密、写入即可。

难的地方,是如何适应系统功能,比如一个条件查询功能:

  • 前端传入的是明文数据,如何匹配数据库中的密文数据;

  • 后端返回的是密文数据,如何解密返回给前端才好;

经分析,可以有以下两种方案,这里以用户表中的password字段为例;

在这里插入图片描述

加解密算法就用Hutool包中SecureUtil中的这两个方法;

	// 加密AES aes = SecureUtil.aes("9D7E445B58C6A275A78165429B42F8D3".getBytes());String encryptHex = aes.encryptHex("123456");System.out.println("密文:" + encryptHex);// 解密String decryptStr = aes.decryptStr(encryptHex);System.out.println("明文:" + decryptStr);

在这里插入图片描述

方案一:拦截器

可以使用MyBatis的拦截器,对参数,封装结果集,一进一出进行拦截,分别执行对应的加密、解密逻辑,即对参数进行加密,与数据库进行匹配,对结果集进行解密返回。

首先,创建一个自定义注解,打在实体类对象的属性上,表示该属性需要脱敏处理;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 数据脱敏注解*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SensitivityField {
}

User实体类对象,password字段需要脱敏;

import com.hezy.annotation.SensitivityField;
import lombok.Data;/*** 用户实体类*/
@Data
public class User implements Serializable {/*** 用户id*/private String id;/*** 用户名*/private String username;/*** 密码*/@SensitivityFieldprivate String password;/*** 部门ID*/private String deptId;
}

编写Mybatis拦截器类

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.hezy.annotation.SensitivityField;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Properties;/*** Mybatis 拦截器*/
@Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class MybatisInterceptor implements Interceptor {/*** 密钥*/private static final String SECRET_KEY = "9D7E445B58C6A275A78165429B42F8D3";/*** 拦截器方法*/@Overridepublic Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException, NoSuchFieldException {Object target = invocation.getTarget();// 1.参数处理if (target instanceof ParameterHandler) {// 获取参数ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();Field declaredField = parameterHandler.getClass().getDeclaredField("parameterObject");declaredField.setAccessible(true);Object parameterObject = declaredField.get(parameterHandler);// 对参数进行加密encryption(parameterObject);return invocation.proceed();}// 2.结果集处理if (target instanceof ResultSetHandler) {// 获取结果集Object resultObject = invocation.proceed();if (resultObject instanceof ArrayList) {ArrayList resultList = (ArrayList) resultObject;for (Object o : resultList) {decryption(o);}return resultList;} else {return resultObject.getClass();}}// 3.其他情况return invocation.proceed();}/*** 包装* @param target* @return*/@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}/*** 设置属性* @param properties*/@Overridepublic void setProperties(Properties properties) {System.out.println("This is setProperties method......");}/*** 加密*/private static void encryption(Object parameterObject) throws IllegalAccessException {// 取出参数,即User对象Class<?> parameterObjectClass = parameterObject.getClass();// 取出对象的所有字段Field[] declaredFields = parameterObjectClass.getDeclaredFields();// 遍历,判断是否有敏感字段for (Field field : declaredFields) {SensitivityField annotation = field.getAnnotation(SensitivityField.class);if (annotation == null) {continue;}field.setAccessible(true);// 有,则进行加密,将对象的字段值进行加密AES aes = SecureUtil.aes(SECRET_KEY.getBytes());field.set(parameterObject, new String(aes.encryptHex(field.get(parameterObject).toString().getBytes())));}}/*** 解密*/private static void decryption(Object o) throws IllegalAccessException {// 取出返回的结果集对象的所有字段Field[] declaredFields = o.getClass().getDeclaredFields();// 遍历,判断是否有敏感字段for (Field field : declaredFields) {SensitivityField annotation = field.getAnnotation(SensitivityField.class);if (annotation == null) {continue;}field.setAccessible(true);// 有,则进行解密,将对象的字段值进行解密AES aes = SecureUtil.aes(SECRET_KEY.getBytes());Object value = field.get(o);field.set(o, new String(aes.decryptStr(value.toString())));}}
}

配置类,将拦截器对象装配到IOC容器中

import com.hezy.interceptor.MybatisInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** Mybatis拦截器配置*/
@Configuration
public class MybatisInterceptorConfig {@Beanpublic MybatisInterceptor mybatisInterceptor() {return new MybatisInterceptor();}
}

测试一下,新增一个User对象,如下:

    @Testpublic void insertUser() {User user = new User();user.setId("5");user.setUsername("测试");user.setPassword("123456");userMapper.insertUser(user);}

insertUser()方法

    @Insert("insert into tb_user (id, username, password, dept_id) values (#{id}, #{username}, #{password}, #{deptId})")void insertUser(User user);

运行,没得问题,明文数据,落库就是密文;

在这里插入图片描述

在这里插入图片描述

再试下查询(根据密码查询,感觉怪怪的,嘿嘿)

	@Testpublic void queryUser() {User user = new User();user.setPassword("123456");System.out.println(userMapper.selectUserByPassword(user));}

selectUserByPassword()方法

    User selectUserByPassword(User user);

UserMapper.xml

    <select id="selectUserByPassword" resultType="com.hezy.pojo.User">select * from tb_user where password = #{password}</select>

运行,可以看到password字段是密文传入,明文返回的,说明拦截器起作用了。

在这里插入图片描述

介绍完,再来评价这两种方案。

方案二:AOP

使用AOP+自定义注解,在需要进行处理的Mapper方法上打上注解,然后在AOP中进行加解密的处理。如下:

自定义注解,加载Mapper的方法上,用于识别需要操作的Mapper方法

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 方法级别的敏感字段处理注解*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SensitivityMethod {
}

AOP切面,用于处理具体的参数加密、结果集解密逻辑;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.hezy.annotation.SensitivityField;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;/*** 敏感字段处理切面*/
@Aspect
@Component
public class SensitivityAOP {/*** 密钥*/private static final String SECRET_KEY = "9D7E445B58C6A275A78165429B42F8D3";/*** com.hezy.mapper中的所有方法,或者加上@SensitivityMethod注解的方法*/@Pointcut("@annotation(com.hezy.annotation.SensitivityMethod) || execution(* com.hezy.mapper.*.*(..))")public void pt(){}/*** 环绕通知*/@Around("pt()")public Object aroundAop(ProceedingJoinPoint pjp) throws Throwable {// 1.获取方法参数Object[] args = pjp.getArgs();// 2.对参数进行加密if (args != null && args.length > 0) {encryption(args);}// 3.执行Mapper方法Object result = pjp.proceed();// 4.对结果集进行解密if (result == null) {return null;}decryption(result);// 5.返回return result;}/*** 加密*/private static void encryption(Object[] args) throws IllegalAccessException {for (Object arg : args) {// 取出参数的所有字段Field[] declaredFields = arg.getClass().getDeclaredFields();// 遍历字段for (Field declaredField : declaredFields) {SensitivityField annotation = declaredField.getAnnotation(SensitivityField.class);if (annotation == null) {continue;}// 如果是敏感字段,就加密declaredField.setAccessible(true);String string = (String) declaredField.get(arg);AES aes = SecureUtil.aes(SECRET_KEY.getBytes());String encryptHex = aes.encryptHex(string);declaredField.set(arg, encryptHex);}}}/*** 解密*/private static void decryption(Object result) throws IllegalAccessException {// 取出结果集的所有字段Field[] declaredFields = result.getClass().getDeclaredFields();// 遍历字段for (Field field : declaredFields) {SensitivityField annotation = field.getAnnotation(SensitivityField.class);if (annotation == null) {continue;}// 如果是敏感字段,就解密field.setAccessible(true);String string = (String) field.get(result);AES aes = SecureUtil.aes(SECRET_KEY.getBytes());String encryptHex = aes.decryptStr(string);field.set(result, encryptHex);}}
}

细节的地方与方案一差不多,测试一下新增和查询;

(新增)

在这里插入图片描述

(查询)

在这里插入图片描述

(效果一模一样)

在这里插入图片描述

讨论

直接说结论,推荐使用方案二。方案一对所有操作数据库的Mapper方法都拦截,不够灵活,而方案二使用切入点表达式 + 自定义注解,可只对特定方法进行操作。在需要脱敏的表比较多,Mapper方法多的情况下,一个一个加注解可能比较麻烦,但灵活,有操作的空间。

另外,我们这里讨论的加密方案,只涉及到以实体类对象为参数传入,以实体类对象为结果集返回,并没有考虑到仅传递一个敏感字段值为参数,或者返回一个Map对象,Map中存储一个敏感字段值,如 password:833124569c4f911b0181212681c0fee2,这些情况就需要我们根据项目中现存的Mapper方法去写对应的业务逻辑。考虑到扩展性,方案二也要更合适一点。

(仅传递一个敏感字段值)

    @Testpublic void queryAll() {String password1 = "123456";System.out.println(userMapper.selectAllByPassword(password1));}
    List<User> selectAllByPassword(String password);
    <select id="selectAllByPassword" resultType="com.hezy.pojo.User">select * from tb_user where password = #{password}</select>

(无法查到我们想要查询的记录)

在这里插入图片描述

在这里插入图片描述

而且,方案一依赖于Mybatis框架,方案二用的是Spring的特性

总结

本文介绍了数据加密的两种方案

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

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

相关文章

Spring Security——11,自定义权限校验方法

自定义权限校验方法 一键三连有没有捏~~ 我们也可以定义自己的权限校验方法&#xff0c;在PreAuthorize注解中使用我们的方法。 自定义一个权限检验方法&#xff1a; 在SPEL表达式中使用 ex相当于获取容器中bean的名字未ex的对象。然后再调用这个对象的 hasAuthority方法&am…

软考高级架构师:嵌入式系统的内核架构

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

ssm028蜀都天香酒楼的网站设计与实现+jsp

基于JSP的蜀都天香酒楼管理系统的设计与实现 摘要 近年来&#xff0c;信息化管理行业的不断兴起&#xff0c;使得人们的日常生活越来越离不开计算机和互联网技术。首先&#xff0c;根据收集到的用户需求分析&#xff0c;对设计系统有一个初步的认识与了解&#xff0c;确定蜀都…

带头双向循环链表实现

1.结构及特性 前面我们实现了无头单向非循环链表&#xff0c;它的结构是这样的&#xff1a; 在这里的head只是一个指向头结点的指针&#xff0c;而不是带头链表的头节点。 而带头双向循环链表的逻辑结构则是这样的 这就是链表的结构&#xff0c;链表的每一个节点都有两个指针…

docker 安装redis报错:can not init background jbos

启动redis&#xff0c;发现一直再重启 docker run -d --name redis -p 6379:6379 --restartalways redis:6.2.6 --requirepass "123456" 查看日志&#xff0c;发现job没启动 docker logs 47f6572a779c 尝试了一堆解决办法。。。最后发现尝试安装了redis6.2.6版本&a…

从《布瓦尔与佩库歇》实践中学习社会科学概论

从《布瓦尔与佩库歇》实践中学习社会科学概论 前情提要《布瓦尔与佩库歇》实践笔记云藏山鹰社会科学概论报告核心--信息形数身知™意合™意气实体过程意气实体过程宇宙学诠释™ 社会科学概论花间流风版导读&#xff0c;马斯克风格演讲[ 一尚韬竹团队供稿&#xff1b;] 内容展开…

实验4 层次图和HIPO图

一、实验目的 通过绘制层次图和HIPO图&#xff0c;熟练掌握层次图和HIPO图的基本原理。 能对简单问题进行层次图和HIPO图的分析&#xff0c;独立地完成层次图和HIPO图设计。 二、实验项目内容&#xff08;实验题目&#xff09; 1、用Microsoft Visio绘制出图书馆管理系统的层…

Flume 拦截器概念及自定义拦截器的运用

文章目录 Flume 拦截器拦截器的作用拦截器运用1.创建项目2.实现拦截器接口3.编写事件处理逻辑4.拦截器构建5.打包与上传6.编写配置文件7.测试运行 Flume 拦截器 在 Flume 中&#xff0c;拦截器&#xff08;Interceptors&#xff09;是一种可以在事件传输过程中拦截、处理和修改…

Spring定义Bean对象笔记(二)

前言&#xff1a;上一篇记录了通过XML文件来定义Bean对象&#xff0c;这一篇将记录通过注解和配置类的方式来定义Bean对象。 核心注解&#xff1a; 定义对象&#xff1a;Component,Service,Repository,Controller 依赖注入&#xff1a; 按类型&#xff1a;Autowired 按名称&am…

【JavaScript】作用域 ③ ( JavaScript 作用域链 | 作用域链变量查找机制 )

文章目录 一、JavaScript 作用域链1、作用域2、作用域链3、作用域链变量查找机制 二、代码示例 - 作用域链 一、JavaScript 作用域链 1、作用域 在 JavaScript 中 , 任何代码都有 作用域 , 全局作用域 : 在 <script> 标签中 或者 js 脚本中 定义的变量 属于 全局作用域 …

Vue3【进阶】

简介 https://cn.vuejs.org/guide/introduction.html 创建vue3工程 【基于 vue-cli创建】 基本和vue-cli的过程类似&#xff0c;只是选择的时候用vue3创建 【基于vite创建】【推荐】 【官网】https://vitejs.cn/ 【可以先去学一下webpack】 步骤 【https://cn.vitejs.…

kubernetes集群添加到jumpserver堡垒机里管理

第一步、在kubernetes集群中获取一个永久的token。 jumpserver堡垒机用api的来管理kubernetes&#xff0c;所以需要kubernetes的token&#xff0c;这个token还需要是一个永久的token&#xff0c;版本变更&#xff1a;Kubernetes 1.24基于安全方面的考虑&#xff08;特性门控Le…

LeetCode-热题100:118. 杨辉三角

题目描述 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示例 2: 输入: numRows 1 输出: [[1]]…

代码随想录第32天|455.分发饼干 376. 摆动序列

理论基础 贪心算法核心&#xff1a;选择每一阶段的局部最优&#xff0c;从而达到全局最优。 455.分发饼干 455. 分发饼干 - 力扣&#xff08;LeetCode&#xff09;代码随想录 (programmercarl.com)455. 分发饼干 - 力扣&#xff08;LeetCode&#xff09; 贪心算法理论基础&am…

【AI绘画/作图】风景背景类关键词模板参考

因为ds官网被墙,所以翻了IDE的源码整理了下stablestudio里的官方模板&#xff0c;顺便每个模板生成了一份…不知道怎么写关键词的可以参考 Stunning sunset over a futuristic city, with towering skyscrapers and flying vehicles, golden hour lighting and dramatic cloud…

C语言高效的网络爬虫:实现对新闻网站的全面爬取

1. 背景 搜狐是一个拥有丰富新闻内容的网站&#xff0c;我们希望能够通过网络爬虫系统&#xff0c;将其各类新闻内容进行全面地获取和分析。为了实现这一目标&#xff0c;我们将采用C语言编写网络爬虫程序&#xff0c;通过该程序实现对 news.sohu.com 的自动化访问和数据提取。…

行业型软文怎么写,媒介盒子分享

行业型软文即指面对行业内人群的软文,此类文章的目的通常是为了扩大行业影响力,奠定行业品牌地位。企业的行业地位将直接影响到其核心竞争力,甚至会影响到最终用户的选择。今天媒介盒子就和大家聊聊行业型软文怎么写。 一、了解受众需求&#xff1a; 首先&#xff0c;深入研究…

【C++第三阶段】string容器

以下内容仅为当前认识&#xff0c;可能有不足之处&#xff0c;欢迎讨论&#xff01; 文章目录 string容器基本概念构造函数赋值操作拼接操作字符串查找和替换字符串比较字符串存取字符串插入和删除字符串子串 string容器 基本概念 本质&#x1f449;string是C风格的字符串&…

快速获取文件夹及其子文件夹下的所有文件名

1、在文件夹中新建文本文档&#xff0c;命名为“命令.txt” 2、输入以下内容 tree /F > 文件名.txt dir *.* /B > 文件名.txt 其中文件名和文件格式可以是任意的&#xff0c;tree命令可生成文件及其子文件夹下所有文件的名称&#xff0c;dir命令只生成当前目…

网络基础三——初识IP协议

网络基础三 ​ 数据通过应用层、传输层将数据传输到了网络层&#xff1b; ​ 传输层协议&#xff0c;如&#xff1a;TCP协议提供可靠性策略或者高效性策略&#xff0c;UDP提供实时性策略&#xff0c;保证向下层交付的数据是符合要求的的&#xff1b;而网络层&#xff0c;如&a…