目录

前言

场景分析

实现方案

环境配置

依赖配置

代码实现

总结


前言

在《Springboot项目如何设计接口中敏感字段的加密、解密》和《Springboot项目如何设计接口中敏感数据的脱敏展示?》中,分别分享了Springboot项目中,在遇到一些敏感字段需要加密、脱敏的业务场景的时候,如何使用AOP(面向切面编程)来实现。那么有一个问题不知道大家想过没?敏感字段数据是加密存储在数据库的表中,如果需要对这些敏感字段进行模模糊查询,还用原来的通过sql的where从句的like来模糊查询的方式肯定是不行的,那么应该怎么实现呢?这篇文章就来解决这个问题。

场景分析

假如有类似这样的一个场景:有一个人员管理的功能,人员信息列表的主要字段有姓名、性别、用户账号、手机号码、身份证号码、家庭住址、注册日期等,可以对任意一条数据进行增、删、改、查,其中姓名、身份证号码、手机号码字段要支持模糊查询。

简单分析一个场景,可以知道:手机号码、身份证号码、家庭人址字段数据是敏感数据,这些字段的数据是要加密存储在数据库里,在页面上展示的时候需要进行脱敏处理的。如果用户想要查询真实姓名是包含有“张三”的所有人员信息,可以在页面上输入一个关键字,如“张三”,点击开始查询后,这个参数会传递到后台,后台会执行一条sql,如“select * from sys_person where real_name like ‘%张三%’”,执行结果中包含了所有用户真实姓名包含有“张三”的所有数据记录,如“张三”,“张三丰”等。如果用户要查询手机号码尾号是“0537”的用户,后台执行类似与姓名模糊查询的sql,"select * from sys_person where phone like '%0537'",肯定是得不到正确的结果的,因为手机号码字段在数据库中的数据是加密后的结果,而‘0537’是明文。身份证号码、家庭住址等其他敏感字段在模糊查询的时候也都有类似这样的问题,这也是敏感字段模糊查询的痛点,即模糊查询关键字与实际存储的数据不一致。

实现方案

下面分享几种解决方案:

第一种,先解密再查询

查询出目标表内所有的数据,在内存中对要模糊查询的敏感字段的加密数据进行解密,然后再遍历解密后的数据,与模糊查询关键字进行比较,筛选出包含有模糊查询关键字的数据行。

这种方法是最容易想到的,但有一个比较明显的问题是,模糊查询的过程是在内存中进行的,如果数据量特别大,很容易导致内存溢出,因此不推荐在生产中使用这种方法;

第二种,明文映射表

新建一张映射表,存储敏感字段解密后的数据与目标表主键的映射表,需要模糊查询的时候,先对明文映射表进行模糊查询,得到符合条件的目标数据的主键,再返回来根据主键查询目标表;

这种方法,实际上是有点掩耳盗铃的感觉,敏感字段加密存储的字段主要是考虑到安全性,使用明文映射表来存储解密后的敏感字段,实际上相当于敏感字段没有加密存储,与最被要对敏感字段加密的初衷相违背,因此不推荐在生产中使用这种方法;

第三种,数据库层面进行解密查询

后台在执行查询sql时对敏感字段先解密,然后再执行like,以上面的人员管理列表模糊查询为例,即对sql的改造为:“select * from sys_person where AES_DECRYPT(phone,'key') like '%0537'”;

这种方法的优点是,成本比较小,容易实现,但是缺点很明显,该字段无法通过数据库索引来优化查询,另外有一些数据库无法保证数据库的加解密算法与程序的加解密算法一致,可能会导致可以程序中加密,但是无法在数据库中解密的或者可以在数据库加密无法在程序中解密的问题,因此不推荐在生产中使用这种方法;

第四种,分词密文映射表

这种方法是对第二种思路的基础上进行延伸优化,也是主流的方法。新建一张分词密文映射表,在敏感字段数据新增、修改的后,对敏感字段进行分词组合,如“15503770537”的分词组合有“155”、“0377”、“0537”等,再对每个分词进行加密,建立起敏感字段的分词密文与目标数据行主键的关联关系;在处理模糊查询的时候,对模糊查询关键字进行加密,用加密后的模糊查询关键字,对分词密文映射表进行like查询,得到目标数据行的主键,再以目标数据行的主键为条件返回目标表进行精确查询。

图片一:分组组合加密前

图片二:分组组合加密后

淘宝、阿里、拼多、京东等大厂对用户敏感数据加密后支持模糊查询都是这样的原理,下面是几个大厂的敏感字段模糊查询方案说明,有兴趣可以了解一下:

厂商

链接

淘宝密文字段检索方案

https://open.taobao.com/docV3.htm?docId=106213&docType=1

阿里巴巴文字段检索方案

https://jaq-doc.alibaba.com/docs/doc.htm?treeId=1&articleId=106213&docType=1

拼多多密文字段检索方案

https://open.pinduoduo.com/application/document/browse?idStr=3407B605226E77F2

京东密文字段检索方案

https://jos.jd.com/commondoc?listId=345

这种方法的优点就是原理简单,实现起来也不复杂,但是有一定的局限性,算是一个对性能、业务相折中的一个方案,相比较之下,在能想的方法中,比较推荐这种方法,但是要特别注意的是,对模糊查询的关键字的长度,要在业务层面进行限制;以手机号为例,可以要求对模糊查询的关键字是四位或者是五位,具体可以再根据具体的场景进行详细划分。为什么要增加这样的限制呢?因为明文加密后长度为变长,有额外的存储成本和查询性能成本,分词组合越多,需要的存储空间以及所消耗的查询性能成本也就更大,并且分词越短,被硬破解的可能性也就越大,也会在一定程度上导致安全性降低;

环境配置

jdk版本:1.8开发工具:Intellij iDEA 2020.1

springboot:2.3.9.RELEASE

mybatis-spring-boot-starter:2.1.4

依赖配置

示例主要用到了SpringAop,加密是对称加密,用到了hutool工具包里的加密解密工具类,也可以使用自己封装的加密解密工具类。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.3</version>
</dependency>

代码实现

1、新建分词密文映射表;

如果是多个模糊查询的字段,可以共用在一张分词密文映射表中扩展多个字段,以示例中的人员管理功能为例,新建sys_person_phone_encrypt表(人员的手机号码分词密文映射表),用于存储人员id与分词组合密文的映射关系

create table if not exists sys_person_phone_encrypt
(id bigint auto_increment comment '主键' primary key,person_id int not null comment '关联人员信息表主键',phone_key varchar(500) not null comment '手机号码分词密文'
)
comment '人员的手机号码分词密文映射表';

2、敏感字段数据在保存入库的时候,对敏感字段进行分词组合并加密码,存储在分词密文映射表;

在注册人员信息的时候,先取出通过AOP进行加密过的手机号码进行解密;手机号码解密之后,对手机号码按照连续四位进行分词组合,并对每一个手机号码的分词进行加密,最后把所有的加密后手机号码分词拼接成一个字符串,与人员id一起保存到人员的手机号码分词密文映射表;

public Person registe(Person person) {this.personDao.insert(person);String phone = this.decrypt(person.getPhoneNumber());String phoneKeywords = this.phoneKeywords(phone);this.personDao.insertPhoneKeyworkds(person.getId(),phoneKeywords);return person;
}
private String phoneKeywords(String phone) {String keywords = this.keywords(phone, 4);System.out.println(keywords.length());return keywords;
}//分词组合加密
private String keywords(String word, int len) {StringBuilder sb = new StringBuilder();for (int i = 0; i < word.length(); i++) {int start = i;int end = i + len;String sub1 = word.substring(start, end);sb.append(this.encrypt(sub1));if (end == word.length()) {break;}}return sb.toString();
}
public String encrypt(String val) {//这里特别注意一下,对称加密是根据密钥进行加密和解密的,加密和解密的密钥是相同的,一旦泄漏,就无秘密可言,//“fanfu-csdn”就是我自定义的密钥,这里仅作演示使用,实际业务中,这个密钥要以安全的方式存储;byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(), "fanfu-csdn".getBytes()).getEncoded();SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.DES, key);String encryptValue = aes.encryptBase64(val);return encryptValue;
}
public String decrypt(String val) {//这里特别注意一下,对称加密是根据密钥进行加密和解密的,加密和解密的密钥是相同的,一旦泄漏,就无秘密可言,//“fanfu-csdn”就是我自定义的密钥,这里仅作演示使用,实际业务中,这个密钥要以安全的方式存储;byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(), "fanfu-csdn".getBytes()).getEncoded();SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.DES, key);String encryptValue = aes.decryptStr(val);return encryptValue;
}

3、模糊查询的时候,对模糊查询关键字进行加密,以加密后的关键字密文为查询条件,查询密文映射表,得到目标数据行的id,再以目标数据行id为查询条件,查询目标数据表;

根据手机号码的四位进行模糊查询的时候,以加密后模糊查询的关键字为条件,查询sys_person_phone_encrypt表(人员的手机号码分词密文映射表),得到人员信息id;再以人员信息id,查询人员信息表;

public List<Person> getPersonList(String phoneVal) {if (phoneVal != null) {return this.personDao.queryByPhoneEncrypt(this.encrypt(phoneVal));}return this.personDao.queryList(phoneVal);
}
<select id="queryByPhoneEncrypt" resultMap="personMap">select * from sys_person where id in (select person_id from sys_person_phone_encryptwhere phone_key like concat('%',#{phoneVal},'%'))
</select>

图片三: 模糊查询“6666”

总结

至此,《Springboot项目如何设计接口中敏感字段的加密、解密》、《Springboot项目如何设计接口中敏感数据的脱敏展示?》和《Springboot项目如何设计接口中敏感字段的模糊查询?》三篇文章,完成了敏感字段如何加密、解密、以及加密后的字段如何进行模糊查询的梳理总结。

示例完整代码:https://gitcode.net/fox9916/fanfu-web.git(branch:敏感字段加密、解密、脱敏 )

Springboot项目如何设计接口中敏感字段模糊查询?相关推荐

  1. 记录springBoot项目使用jar包中的mapper报错问题

    记录springBoot项目使用jar包中的mapper报错问题 背景 双数据源的实现 启动时报找不到对应的service 启动报mapper找不到 启动报no sqlsessionfactory 启 ...

  2. MySQL单表多字段模糊查询解决方法

    2019独角兽企业重金招聘Python工程师标准>>> 在最近的一个项目需要实现在单表中对多字段进行多个关键字的模糊查询,但这数个关键字并不一定都存在于某个字段 例如现有table表 ...

  3. MySQL多重条件模糊查询_简单实现mysql多字段模糊查询

    在后台的项目中,会经常遇到,就是信息的查找,这里就举个简单的例子,如搜索某一个产品的信息,在一个表里面有多个字段,而搜索的内容可能是其中的某个字段,这样只有多字段查询才能实现. MySQL多字段模糊查 ...

  4. MySQL单表多字段模糊查询

    今天工作时遇到一个功能问题:就是输入关键字搜索的字段不只一个字段,比如 我输入: 超天才 ,需要检索出 包含这个关键字的 name . company.job等多个字段.在网上查询了一会就找到了答案. ...

  5. oracle查询blob模糊搜索,BLOB字段模糊查询

    1. BLOB字段模糊查询报错 blob字段直接用select   * from  table_name  where  column  like'%%'查找的时候是不能实现的,主要是字段类型不符.不 ...

  6. MYSQL中LIKE(模糊查询)

    文章目录 0 写在前面 1 代码格式 2 SQL数据准备 3 举例 3.1格式一:`[NOT] where 字段 LIKE '%' `:表示指代任意内容: 3.2 格式四:[NOT] where 字段 ...

  7. python数据库模糊查询_python中数据库like模糊查询方式

    python中数据库like模糊查询方式 在Python中%是一个格式化字符,所以如果需要使用%则需要写成%%. 将在Python中执行的sql语句改为: sql = "SELECT * F ...

  8. mysql 加密脱敏字段模糊查询 笔记

    mysql 加密脱敏字段模糊查询 入库的数据是要进行加密脱敏的数据:但是这个字段还要支持界面模糊匹配:这里做个笔记吧 仅供参考 insert into 的sql 大概长这样 insert into t ...

  9. Sql语句、多个字段模糊查询、

    Sql语句收获 1. select * from 表 order by time asc 最早时间在前 select * from 表 order by time desc 最新时间在前 2.模糊查询 ...

最新文章

  1. CSS sprites
  2. phpstorm + xdebug 远程断点调试
  3. MFC 获取命令行参数
  4. 【转】[Java] HashMap使用的示例
  5. vs2015 支持Android arm neon Introducing Visual Studio’s Emulator for Android
  6. java 获取对象方法有哪些方法有哪些方法有哪些_Java中创建String 对象的方法有哪些...
  7. 斐波那契数列 青蛙跳台阶 变态跳台阶
  8. 云服务器php版本修改,云服务器 更改php版本
  9. Zabbix weChat告警防坑要点
  10. CWP中的排序算法:C语言
  11. Dataguard后台进程解析
  12. TCP/IP 详解笔记
  13. php 获取 get变量,php – 在Laravel中获取GET“?”变量
  14. [Ubuntu] 使用 ibus 输入汉字
  15. 找工作?最容易遇到的Java面试题
  16. 《加速器理论(第二版)》读书笔记
  17. JSON.stringify初步使用
  18. OracleEBSAPP-XLA-95103错误(EventIDcannothaveaNULLvalue.)
  19. 什么是复制和交换成语?
  20. 毕设之基金定投小程序

热门文章

  1. 如何套用html模板文件,套用DEMO教程
  2. 我是如何使用 Python 优雅的薅到网易uu的羊毛的
  3. java反射 getMethod_Java反射之Method Class.getMethod(String name, Class?... parameterTypes)
  4. mac 创建Vue3项目
  5. 华为荣耀android是什么系统,华为荣耀Android4.0系统正式发布
  6. 实操:只有6000粉丝的公众号,流量主收入1500
  7. cout.put()的用法
  8. STL 配置器(allocator)
  9. Spring - Netty原理剖析
  10. 工行闪酷卡 网银圈存