前提介紹 (開發(fā)環(huán)境+需求)
1. 開發(fā)框架、環(huán)境
springboot+mybatis-plus+mysql5.7(oracle應該也是可以的,沒有測試,但實現思路是都可以滿足,懶得測oracle了,哈哈)
2. 需求介紹(背景)
需求很簡單: 就是將數據存儲到數據庫,并且將敏感數據字段進行加密處理保存(比如:身份證,手機號,銀行卡 等等)
需求也很變態(tài):加密的數據要模糊搜素??!
如果需求不需要模糊搜素,直接加密入庫就完事了,直接看這篇文章 mybatis-plus進行數據字段加密解密入庫 ,就可以了!
3.設計思路
個人 采用 映射表 分詞的 方案進行處理的
mysql 創(chuàng)建 加密 模糊搜索字段 ,將字段加密進行分詞處理,保存到 搜索映射表
分詞這邊采用 es 使用的ik分詞器,原因就是:自己寫一個分詞是不可能的了,算法沒那么牛逼 ??! ,并且ik 分詞器可以自定義詞語進行分詞
然后再使用 用 mybaitis-plus 自帶的注解 @TableField(typeHandler = TypeHandler.class)
寫一個 handle 類繼承 BaseTypeHandler ,將數據進行加解密
大致思路是這樣 !
4. 具體實現
4.1 . 依賴
<!-- 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>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.12</version>
</dependency>
<!-- AES加密解密需要包-->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
4.2 繼承Mybatis的 BaseTypeHandler類,重寫方法
package com.xiarp.encryptstorage.handler;
import com.xiarp.encryptstorage.util.AesUtil;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.*;
/**
* @author xiarp
*/
public class TypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, AesUtil.encrypt(parameter));
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return AesUtil.decrypt(rs.getString(columnName));
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return AesUtil.decrypt(rs.getString(columnIndex));
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return AesUtil.decrypt(cs.getString(columnIndex));
}
}
4.3 AES 加密的工具類
package com.xiarp.encryptstorage.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* aes 加密的工具類
* 1.存儲 加密的秘鑰key
* 2.實現 aes 加密
* 3.實現aes解密的功能
* @author xiarp
*/
@Slf4j
public class AesUtil {
/**
* 定義 aes 加密的key
* 密鑰 必須是16位, 自定義,
* 如果不是16位, 則會出現InvalidKeyException: Illegal key size
* 解決方案有兩種:
* 需要安裝Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files(可以在Oracle下載).
* .設置設置key的長度為16個字母和數字的字符竄(128 Bit/8=16字符)就不報錯了。
*/
private static final String KEY = "KEYBYACSJAVAZXLL";
/**
* 偏移量
*/
private static final int OFFSET = 16;
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
private static final String ALGORITHM = "AES";
/**
* 加密
* @param content content
* @return String
*/
public static String encrypt(String content) {
return encrypt(content, KEY);
}
/**
* 解密
*
* @param content content
* @return String
*/
public static String decrypt(String content) {
return decrypt(content, KEY);
}
/**
* 加密
*
* @param content 需要加密的內容
* @param key 加密密碼
* @return String
*/
public static String encrypt(String content, String key) {
try {
SecretKeySpec skey = new SecretKeySpec(key.getBytes(), ALGORITHM);
IvParameterSpec iv = new IvParameterSpec(key.getBytes(), 0, OFFSET);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
//定義加密編碼
String charset = "utf-8";
byte[] byteContent = content.getBytes(charset);
// 初始化
cipher.init(Cipher.ENCRYPT_MODE, skey, iv);
byte[] result = cipher.doFinal(byteContent);
// 加密
return new Base64().encodeToString(result);
} catch (Exception e) {
log.debug("加密失敗:{}",e.getMessage());
}
return null;
}
/**
* AES(256)解密
*
* @param content 待解密內容
* @param key 解密密鑰
* @return 解密之后
*/
public static String decrypt(String content, String key) {
try {
SecretKeySpec skey = new SecretKeySpec(key.getBytes(), ALGORITHM);
IvParameterSpec iv = new IvParameterSpec(key.getBytes(), 0, OFFSET);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
// 初始化
String charset = "utf-8";
cipher.init(Cipher.DECRYPT_MODE, skey, iv);
byte[] result = cipher.doFinal(new Base64().decode(content));
// 解密
return new String(result,charset);
} catch (Exception e) {
log.debug("解密失敗:{}",e.getMessage());
}
return null;
}
}
4.4 分詞器 工具類 (部分分詞 + ik【可自定義擴展分詞】)以及使用介紹
- 工具類
package com.xiarp.encryptstorage.util;
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;
/**
* @author xiarp
*/
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;
}
}
- ik 分詞器 配置文件+ 自定義擴展分詞文件+ 不需要分詞文件
文件1: IKAnalyzer.cfg.xml
<?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>
文件2: extend.dic (擴展詞典) ,沒有使用是空白的
文件3: stopword.dic (擴展停止詞典,不要這些 分詞) 沒有使用是空白的
文件全部創(chuàng)建在resources 下
4.4.1 分詞器工具類使用解析,測試
ik 分詞器 (智能分詞)
參數1: 需要分詞的字符串,
參數2: 結果保留幾個字符以上字符串
這邊獲取了 字符 >=2 的所有分詞數據
擴展詞典 :
現在我在 extend.dic (擴展詞典) 文件中加上 “暴富的夢” 跟 “今天是星期五” ,看看結果
再次運行剛才測試代碼 ,可以看到分詞加進去了
取消擴展詞典:
就是這個詞,不要出現,比如分詞結果不要 “星期五” 這個詞出現
再次執(zhí)行
ik 分詞用來分詞手機號數字串類型的不太友好 (有處理方法,可以改ik 的工具類,這邊就不改了,懶!),因此 可以 簡單寫了個 第二種方法,不知道叫什么,就叫部分分詞了
參數同理
是按照,從第0位 開始 取三位,然后 從 第1 位開始取三位 。。。。。 以此類推,直到結束
【注意】:這邊涉及到一個性能和安全問題,比如分詞的字符長度設置的太長,加密又不安全,設置的太短,有影響性能,耗費的存儲空間又多,因此,選擇合適的分詞長度 很重要 (數據量過小不用考慮)
就比如手機號就可以設置成 4 位,為一個分片,模糊搜索也可以說明 “請輸入手機號后四位查詢”
4.5 SQL 創(chuàng)建 分詞 映射表(word_part_mapping) 以及模擬數據 用戶(sys_user) 表
使用
對應需要加密實體類加上注解
簡單模擬數據 新增 查詢
新增數據 需加密字段分詞處理邏輯 (映射表)
再提一句:不要再循環(huán)里添加數據,要批量?。?!
**查詢 數據 **
我向數據庫中 添加了五條數據
以加密的形式存在
查詢結果是明文【符合加密存儲,明文輸出要求】
模糊搜索
輸入一個參數,輸出三條數據,符合單模糊
輸入多個參數 ,輸出符合的兩條數據,符合 多模糊
修改數據 直接看 這篇文章即可,一樣的
注意一點是,修改到了敏感數據,需要先刪除原先敏感數據的分詞,重新分詞進行添加?。?,所以前邊說的 批量添加分詞映射表數據 可以 自己寫一個工具類!!文章來源:http://www.zghlxwxcb.cn/news/detail-704176.html
文章有遺漏的工具類,或者有需要的其他信息,可以打在評論區(qū)!! 歡迎討論指正,當然要是有更好的實現 方案,請指教!??!文章來源地址http://www.zghlxwxcb.cn/news/detail-704176.html
到了這里,關于mybatis-plus 數據字段進行加解密入庫,且加密字段支持模糊搜索的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!