mybatis-plus加密字段模糊查询

目录

前言

设计思路

添加依赖

代码

分词工具类

分词实体类

分词扩展配置

Service类

分词service

角色service 

目录结构

测试

添加

查询

​编辑

结束语


前言

在前面的文章中,实现了数据库中表字段的加解密,mybatis-plus数据库字段信息加解密-CSDN博客,提高了数据的安全性,正当我们松了一口气的时候,这时候产品带来了一个新的需求,说老板需要对这些加密的字段进行模糊查询,一听到这个需求的时候是不是一脸懵逼,对一个加密的字段模糊查询,这是得多久的老血栓才能提出来的要求,不过想想还没有干巴巴的口袋,咬咬牙必须给它实现了。

设计思路

方式一:我们就将当前表信息全部加载到内存中,将加密字段进行解密后,然后在进行模糊匹配。这个针对表中的数据量少的情况下比较好,但是数据量多的话,那么就不适应了。

方式二:新建一张表,将原文信息保存到该表中,加密字段保存在原表中,模糊查询的时候,查询的是新建的表中原文的字段,不过这样就失去了加密字段的意义,不可取。

方式三:新建一张分词表,将加密字段的分词加密信息保存到表中,模糊查询的时候查询该表,获取加密字段数据所在的id集合,然后根据id集合去查询原表。

添加依赖

<!--ik分词器-->
<dependency>
   <groupId>com.janeluo</groupId>
   <artifactId>ikanalyzer</artifactId>
   <version>2012_u6</version>
   <exclusions>
      <exclusion>
         <groupId>org.apache.lucene</groupId>
         <artifactId>lucene-core</artifactId>
      </exclusion>
      <exclusion>
         <groupId>org.apache.lucene</groupId>
         <artifactId>lucene-queryparser</artifactId>
      </exclusion>
      <exclusion>
         <groupId>org.apache.lucene</groupId>
         <artifactId>lucene-analyzers-common</artifactId>
      </exclusion>
   </exclusions>
</dependency>

代码

分词工具类

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import org.wltea.analyzer.core.IKSegmenter;
import org.wltea.analyzer.core.Lexeme;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * @Description: 分词工具类
 * @Author ly
 * @Date 2023/11/222:23
 */
public class AnalyzerUtil {
    /**
     * ik 字符串用该方法
     *
     * @param str    str
     * @param length length
     * @return List<String>
     */
    public static List<String> ikSegmentationList(String str, Integer length) {
        List<String> list = new LinkedList<>();
        try {
            if (StrUtil.isEmpty(str)) {
                return ListUtil.empty();
            }
            StringReader stringReader = new StringReader(str);
            IKSegmenter ik = new IKSegmenter(stringReader, false);
            Lexeme le;
            while ((le = ik.next()) != null) {
                String lexemeText = le.getLexemeText();
                if (lexemeText.length() >= length) {
                    list.add(lexemeText);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 部分分词(分割数字类型的字段,如手机号)
     *
     * @param str    str
     * @param length length
     * @return List<String>
     */
    public static List<String> partSegmentationList(String str, Integer length) {
        List<String> list = new ArrayList<>();
        if (StrUtil.isEmpty(str)) {
            return ListUtil.empty();
        }
        int strLength = str.length();
        for (int startIndex = 0; startIndex <= strLength - length; startIndex++) {
            String substring = str.substring(startIndex, startIndex + length);
            list.add(substring);
        }
        return list;
    }

分词实体类

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.lyy.demo3.handler.TypeHandler;
import lombok.Data;

/**
 * @Description: 分词实体类
 * @Author ly
 * @Date 2023/10/1721:48
 */
@Data
@TableName(value = "word_part_mapping",autoResultMap = true)
public class WordPartMapping {

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 表名
     */
    private String tableName;

    /**
     * 分词对应的数据id
     */
    private Long fieldId;

    /**
     * 分词加密字段
     */
    @TableField(typeHandler = TypeHandler.class)
    private String analyzerEncrypt;

    /**
     * 字段名
     */
    private String fieldName;

}

分词扩展配置

在resource目录下添加IKAnalyzer.cfg.xml、ext_dict、ext-stopwords文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
    <comment>IKAnalyzer扩展配置</comment>
    <!--用户的扩展字典 -->
    <entry key="ext_dict">extend.dic</entry>
    <!--用户扩展停止词字典 -->
    <entry key="ext_stopwords">stopword.dic</entry>
</properties>

Service类

分词service
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lyy.demo3.entity.WordPartMapping;
import com.lyy.demo3.mapper.WordPartMappingMapper;
import com.lyy.demo3.service.WordPartMappingService;
import com.lyy.demo3.utils.AesUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @Author ly
 * @Date 2023/11/2 22:35
 */
@Service
public class WordPartMappingServiceImpl extends ServiceImpl<WordPartMappingMapper, WordPartMapping> implements WordPartMappingService {

    @Autowired
    private WordPartMappingMapper wordPartMappingMapper;

    @Override
    public boolean saveBatchWords(List<WordPartMapping> wordPartMappings) {
        return saveBatch(wordPartMappings);
    }

    @Override
    public List<Long> findFieldId(String searchLike) {
        QueryWrapper<WordPartMapping> wrapper = new QueryWrapper<>();
        ArrayList<String> list = new ArrayList<>();
        wrapper.eq("table_name","roles");
        if(StringUtils.isNotBlank(searchLike)){
            String encrypt = AesUtil.encrypt(searchLike);
            wrapper.eq("field_name","username").eq("analyzer_encrypt",encrypt);
            list.add(searchLike);
        }
        if(1 < list.size()){
            wrapper.groupBy("field_id").having("COUNT(field_id) >=" + list.size());
        }
        List<WordPartMapping> selectList = wordPartMappingMapper.selectList(wrapper);
        List<Long> fieldIds = new ArrayList<>();
        if(CollUtil.isNotEmpty(selectList)){
            fieldIds = selectList.stream().map(
                    WordPartMapping::getFieldId
            ).distinct().collect(Collectors.toList());
        }
        return fieldIds;
    }
}
角色service 
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lyy.demo3.entity.Roles;
import com.lyy.demo3.entity.WordPartMapping;
import com.lyy.demo3.mapper.RolesMapper;
import com.lyy.demo3.service.RolesService;
import com.lyy.demo3.service.WordPartMappingService;
import com.lyy.demo3.utils.AnalyzerUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @Author ly
 * @Date 2023/10/1721:54
 */
@Service
public class RolesServiceImpl extends ServiceImpl<RolesMapper, Roles> implements RolesService {

    @Autowired
    private RolesMapper rolesMapper;

    @Autowired
    private WordPartMappingService wordPartMappingService;


    @Override
    public List<Roles> queryLike(String username) {
        List<Long> fieldId = wordPartMappingService.findFieldId(username);
        return rolesMapper.selectBatchIds(fieldId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addRoles(Roles roles) {
        int insert = rolesMapper.insert(roles);
        // 将模糊查询的数据保存到分词表中
        List<String> nameSp = AnalyzerUtil.ikSegmentationList(roles.getUsername(), 2);
        ArrayList<WordPartMapping> words = new ArrayList<>();
        if(CollUtil.isNotEmpty(nameSp)){
            for(String name : nameSp){
                WordPartMapping partMapping = new WordPartMapping();
                partMapping.setTableName("roles");
                partMapping.setFieldId(roles.getId());
                partMapping.setFieldName("username");
                partMapping.setAnalyzerEncrypt(name);
                words.add(partMapping);
            }
        }
        wordPartMappingService.saveBatchWords(words);
        return insert;
    }

目录结构

测试

添加

 

查询

结束语

现在模糊查询加密字段实现了,后面看看怎么实现数据的脱敏展示吧。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis-Plus提供了数据安全保护功能,其中包括字段加密和解密。在使用加密功能时,数据库存储的字段内容会以十六进制格式的密文形式保存。对于条件查询,如果不对密文进行处理,将无法匹配出想要的结果。为了实现条件查询,可以使用SQL的AES_DECRYPT函数对密文进行解密,并进行匹配。需要注意的是,SQL的解密函数只有AES_DECRYPT,因此只适用于AES加密算法。\[3\] 在具体的代码实现中,可以使用MyBatis-Plus的QueryWrapper来构建查询条件。在查询方法中,可以通过调用AES_DECRYPT函数对加密字段进行解密,并与目标值进行匹配。例如,在getAll方法中,可以使用QueryWrapper的like方法来进行模糊查询,同时使用AES_DECRYPT函数对加密字段进行解密,并与目标值进行匹配。最后,返回匹配的结果列表。\[1\] 需要注意的是,加密后的字段内容需要与数据库中存储的密文进行匹配,而不是与明文进行匹配。因此,在进行条件查询时,需要对密文进行解密后再进行匹配。\[2\] 总结起来,MyBatis-Plus加密功能可以保护数据的安全性,通过使用AES_DECRYPT函数对密文进行解密,可以实现条件查询。\[3\] #### 引用[.reference_title] - *1* *3* [MyBatis-Plus加密字段查询(密文检索)](https://blog.csdn.net/tongxin_tongmeng/article/details/128733039)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [MyBatis-Plus数据安全保护(加密解密)](https://blog.csdn.net/tongxin_tongmeng/article/details/128685399)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值