mybatis实现数据加密,加密回显,加密模糊查询的最优解实现
在开发中,我们经常会遇到需要对数据进行加密的场景。如果是少数字段,我们通常会单独对于这些字段进行加解密处理。但是对于很多字段都要进行加密的情况,前一种办法就无法灵活实现了。这时候我们可以通过使用mybatis插件来实现数据的加解密。使用时只需要使用注解标记字段,该字段就可以被自动加密,同时在查询时还可以进行解密。需要注意:密码字段是不能够使用的,因为业务场景的不同,密码一般都是不可逆的,而且密码不

感谢这两篇文章给予的参考帮助:
https://mp.weixin.qq.com/s/v1Uosv-81RNpsvXePRQTIA
https://blog.csdn.net/m0_37635053/article/details/127000204
一.前言
在开发中,我们经常会遇到需要对数据进行加密的场景。如果是少数字段,我们通常会单独对于这些字段进行加解密处理。但是对于很多字段都要进行加密的情况,前一种办法就无法灵活实现了。
这时候我们可以通过使用mybatis插件来实现数据的加解密。使用时只需要使用注解标记字段,该字段就可以被自动加密,同时在查询时还可以进行解密。需要注意:密码字段是不能够使用的,因为业务场景的不同,密码一般都是不可逆的,而且密码不需要查询。
有人会疑问,加密的数据如何进行模糊查询呢?很多人可能会将加密数据在内存中进行解密,然后进行比较,这种只适用于数据量很少的场景。这里有一篇文章:加密后的数据如何进行模糊查询?,大家可以参考。
二.思路
我实现的思路是上述文章中的分词加密,实现的原理很容易理解。加密后的数据肯定是无法直接和用户输入的数据进行比较的,而且因为是模糊查询的原因,用户输入的数据经过加密后也不一定能够匹配密文,这取决于分词的粒度。
而分词加密是将字符串进行分词后,将分词分别进行加密,然后将加密后的密文进行顺序拼接,将拼接后的字符串密文存储在扩展字段中。
三.代码实现
代码可以参考:https://blog.csdn.net/m0_37635053/article/details/127000204,我在这个代码的基础上进行了改进,可以实现加密字段模糊查询。
1.项目代码结构如下:

2.定义3个注解类

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,ElementType.PARAMETER})
public @interface DecryptField {
}
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,ElementType.PARAMETER})
public @interface EncryptField {
/**
* 加密字段对应的关联字段
*
* @return
*/
String extendField() default "";
}
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.TYPE})
public @interface SensitiveData {
}
3.插件配置
package com.xhc.study.config;
import com.xhc.study.plugin.DecryptInterceptor;
import com.xhc.study.plugin.EncryptInterceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
@Configuration
public class MybatisConfig {
@Resource
private DecryptService decryptService;
@Resource
private EncryptService encryptService;
@Bean
public String myInterceptor(SqlSessionFactory sqlSessionFactory) {
sqlSessionFactory.getConfiguration().addInterceptor(new EncryptInterceptor(encryptService));
sqlSessionFactory.getConfiguration().addInterceptor(new DecryptInterceptor(decryptService));
return "interceptor";
}
}
package com.xhc.study.config;
public interface DecryptService {
void decryptSensitiveField(Object targetObj) throws Exception;
}
package com.xhc.study.config;
import org.apache.ibatis.executor.parameter.ParameterHandler;
public interface EncryptService {
void encryptSensitiveField(ParameterHandler parameterHandler) throws NoSuchFieldException, ClassNotFoundException;
}
package com.xhc.study.config.impl;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.xhc.study.config.DecryptService;
import com.xhc.study.entity.util.AESUtil;
import com.xhc.study.entity.util.SensitiveUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.lang.reflect.Field;
import java.util.*;
@Service
public class DecryptServiceImpl implements DecryptService {
/**
* 配置在yaml的密钥
**/
@Value("${aes.key}")
private String key;
@Override
public void decryptSensitiveField(Object targetObj) throws Exception {
if (null == targetObj) {
return;
}
handleDecrypt(targetObj);
}
private void handleDecrypt(Object targetObj) throws Exception {
if (Objects.isNull(targetObj)) {
return;
}
String typeName = targetObj.getClass().getTypeName();
if (typeName.startsWith("com.xhc")) {
doDecryptField(targetObj);
return;
}
handleDecryptCollection(targetObj);
}
/**
* 处理集合
*
* @param result
*/
private void handleDecryptCollection(Object result) throws Exception {
if (result instanceof List) {
handleDecryptList(result);
return;
}
if (result instanceof Map) {
handleDecryptMap(result);
return;
}
if (result instanceof Set) {
handleDecryptSet(result);
return;
}
}
private void handleDecryptSet(Object result) throws Exception {
if (ObjectUtils.isNotEmpty(result)) {
Set<Object> opSet = (HashSet) result;
if (ObjectUtils.isEmpty(opSet)) {
return;
}
for (Object item : opSet) {
if (ObjectUtils.isEmpty(item)) {
continue;
}
handleDecrypt(item);
}
}
}
private void handleDecryptMap(Object result) throws Exception {
if (ObjectUtils.isNotEmpty(result)) {
Map<String, Object> map = (HashMap) result;
if (ObjectUtils.isNotEmpty(map)) {
for (Map.Entry<String, Object> key : map.entrySet()) {
Object mapObject = map.get(key.getKey());
if (ObjectUtils.isEmpty(mapObject)) {
continue;
}
handleDecrypt(mapObject);
}
}
}
}
private void handleDecryptList(Object result) throws Exception {
if (ObjectUtils.isNotEmpty(result)) {
ArrayList<Object> list = (ArrayList) result;
Class<?> aClass = list.get(0).getClass();
if (!SensitiveUtil.isSensitiveClass(aClass)) {
return;
}
List<Field> sensitiveField = SensitiveUtil.getSensitiveField(aClass);
if (CollectionUtils.isNotEmpty(sensitiveField)) {
for (Object obj : list) {
doDecryptField(obj, sensitiveField);
}
}
}
}
private void doDecryptField(Object result) throws Exception {
Class<?> aClass = result.getClass();
if (!SensitiveUtil.isSensitiveClass(aClass)) {
return;
}
List<Field> sensitiveFields = SensitiveUtil.getSensitiveField(aClass);
if (CollectionUtils.isEmpty(sensitiveFields)) {
return;
}
doDecryptField(result, sensitiveFields);
}
private void doDecryptField(Object result, List<Field> sensitiveFields) throws Exception {
for (Field fs : sensitiveFields) {
/**
* 注:这里引用了hutool包 hutool-core
**/
Object fieldValue = ReflectUtil.getFieldValue(result, fs);
if (fieldValue instanceof String) {
if (ObjectUtils.isEmpty(fs)) {
continue;
}
String fieldValueStr = (String) fieldValue;
if (fieldValueStr.startsWith("W86z:")) {
fieldValueStr = AESUtil.decrypt(fieldValueStr.substring("W86z:".length()), key);
}
ReflectUtil.setFieldValue(result, fs, fieldValueStr);
} else {
//递归
handleDecrypt(fieldValue);
}
}
}
}
package com.xhc.study.config.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.xhc.study.anno.EncryptField;
import com.xhc.study.config.EncryptService;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.xhc.study.entity.util.AESUtil;
import com.xhc.study.entity.util.EncryptUtil;
import com.xhc.study.entity.util.SensitiveUtil;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
/**
* 加密
*/
@Service
public class EncryptServiceImpl implements EncryptService {
@Value("${aes.key}")
private String aesKey;
@Autowired
private EncryptUtil encryptUtil;
@Override
public void encryptSensitiveField(ParameterHandler parameterHandler) throws NoSuchFieldException, ClassNotFoundException {
// 得到参数对象
Object parameterObject = parameterHandler.getParameterObject();
if (Objects.isNull(parameterObject)) {
return;
}
if (parameterObject instanceof MapperMethod.ParamMap) {
handleMethodInMapper(parameterHandler);
return;
}
handleMethodNotInmapper(parameterObject);
}
private void handleMethodInMapper(ParameterHandler parameterHandler) throws NoSuchFieldException, ClassNotFoundException {
MapperMethod.ParamMap<Object> paramMap = ( MapperMethod.ParamMap<Object>) parameterHandler.getParameterObject();
for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
String key = entry.getKey();
Object paramValue = entry.getValue();
if (!key.startsWith("param") && Objects.nonNull(paramValue)) {
encrypt(parameterHandler, entry);
}
}
}
/**
* 对DB字段进行加密
*
* @param parameterHandler
* @param entry
*/
private void encrypt(ParameterHandler parameterHandler, Map.Entry<String, Object> entry) throws NoSuchFieldException, ClassNotFoundException {
Object paramValue = entry.getValue();
//字符串
if (paramValue instanceof String) {
handleString(parameterHandler, entry);
return;
}
//请求参数不是字符串
handleMethodNotInmapper(paramValue);
}
/**
* 对象 只处理:1.包含SensitiveData注解,属性包含EncryptField注解 2.属于com.atpingan的包或子包
*
* @param targetObj
*/
private void handleMethodNotInmapper(Object targetObj) {
if (Objects.isNull(targetObj)) {
return;
}
Class<?> parameterClass = targetObj.getClass();
Object entity = targetObj;
Pair<Class<?>, Object> pair = handleQueryWrapper(targetObj);
boolean isQueryWrapperOrLambdaQueryWrapper = Objects.nonNull(pair);
if (isQueryWrapperOrLambdaQueryWrapper) {
parameterClass = pair.getLeft();
entity = pair.getRight();
}
if (Objects.isNull(entity)) {
return;
}
//是否敏感类
boolean sensitiveClass = SensitiveUtil.isSensitiveClass(parameterClass);
if (!sensitiveClass) {
return;
}
//路径前缀判断
String typeName = parameterClass.getTypeName();
if (!typeName.startsWith("com.xhc")) {
return;
}
//获取敏感字段
List<Field> sensitiveFields = SensitiveUtil.getSensitiveField(parameterClass);
if (ObjectUtils.isEmpty(sensitiveFields)) {
return;
}
encryptField(entity, sensitiveFields, parameterClass);
if (isQueryWrapperOrLambdaQueryWrapper) {
ReflectUtil.setFieldValue(targetObj, "entity", entity);
}
}
private void encryptField(Object entity, List<Field> sensitiveFields, Class<?> clazz) {
for (Field fs : sensitiveFields) {
Object fieldValue = ReflectUtil.getFieldValue(entity, fs);
if (fieldValue instanceof String) {
String fieldValueStr = (String) fieldValue;
ReflectUtil.setFieldValue(entity, fs, "W86z:"+AESUtil.encrypt(fieldValueStr, aesKey));
// 获取该字段对应自定义注解里面的信息
Annotation[] declaredAnnotations = fs.getDeclaredAnnotations();
for (Annotation declaredAnnotation : declaredAnnotations) {
if (declaredAnnotation instanceof EncryptField){
EncryptField encryptField = (EncryptField) declaredAnnotation;
// 通过encryptField的值找到对应的属性进行设值
Field targetField = SensitiveUtil.getFieldByName(encryptField.extendField(), clazz);
if (targetField != null) {
// 将需要加密的值进行分词加密
String str = encryptUtil.splitEncryptStr(fieldValueStr);
ReflectUtil.setFieldValue(entity, targetField, str);
}
}
}
}
}
}
private Pair<Class<?>, Object> handleQueryWrapper(Object targetObj) {
if (targetObj instanceof LambdaQueryWrapper) {
return Pair.of(((LambdaQueryWrapper<?>) targetObj).getEntityClass(), ((LambdaQueryWrapper<?>) targetObj).getEntity());
}
if (targetObj instanceof LambdaUpdateWrapper) {
return Pair.of(((LambdaUpdateWrapper<?>) targetObj).getEntityClass(), ((LambdaUpdateWrapper<?>) targetObj).getEntity());
}
if (targetObj instanceof QueryWrapper) {
return Pair.of(((QueryWrapper<?>) targetObj).getEntityClass(), ((QueryWrapper<?>) targetObj).getEntity());
}
return null;
}
private void handleString(ParameterHandler parameterHandler, Map.Entry<String, Object> entry) throws NoSuchFieldException, ClassNotFoundException {
String key = entry.getKey();
Object paramValue = entry.getValue();
Class<? extends ParameterHandler> aClass = parameterHandler.getClass();
Field mappedStatement = aClass.getDeclaredField("mappedStatement");
ReflectUtil.setAccessible(mappedStatement);
MappedStatement statement = (MappedStatement) ReflectUtil.getFieldValue(parameterHandler, mappedStatement);
//方法命名空间
String nameSpace = statement.getId();
if (StringUtils.isBlank(nameSpace)) {
return;
}
String methodName = nameSpace.substring(nameSpace.lastIndexOf(".") + 1);
String className = nameSpace.substring(0, nameSpace.lastIndexOf("."));
Method[] ms = Class.forName(className).getMethods();
Optional<Method> optionalMethod = Arrays.stream(ms).filter(item -> StringUtils.equals(item.getName(), methodName)).findFirst();
if (!optionalMethod.isPresent()) {
return;
}
Method method = optionalMethod.get();
ReflectUtil.setAccessible(method);
//方法参数里面的请求参数注解列表
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
boolean sensitiveField = Arrays.stream(parameterAnnotations).anyMatch(item -> Boolean.TRUE.equals(Arrays.stream(item).anyMatch(ite -> {
if (ite instanceof EncryptField) {
EncryptField sensitive = (EncryptField) ite;
// return StringUtils.equals(key, sensitive.value());
return StringUtils.equals(key, sensitive.toString());
}
return false;
})));
if (!sensitiveField) {
return;
}
String encrypt = "W86z:"+AESUtil.encrypt((String) paramValue, aesKey);
entry.setValue(encrypt);
}
}
package com.xhc.study.config;
import com.xhc.study.plugin.DecryptInterceptor;
import com.xhc.study.plugin.EncryptInterceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
@Configuration
public class MybatisConfig {
@Resource
private DecryptService decryptService;
@Resource
private EncryptService encryptService;
@Bean
public String myInterceptor(SqlSessionFactory sqlSessionFactory) {
sqlSessionFactory.getConfiguration().addInterceptor(new EncryptInterceptor(encryptService));
sqlSessionFactory.getConfiguration().addInterceptor(new DecryptInterceptor(decryptService));
return "interceptor";
}
}
package com.xhc.study.plugin;
import com.xhc.study.config.DecryptService;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.util.Properties;
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class DecryptInterceptor implements Interceptor {
private DecryptService decryptService;
public DecryptInterceptor(DecryptService decryptService) {
this.decryptService = decryptService;
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object result = invocation.proceed();
if (null != result) {
decryptService.decryptSensitiveField(result);
}
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
package com.xhc.study.plugin;
import com.xhc.study.config.EncryptService;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.*;
import java.sql.PreparedStatement;
import java.util.Properties;
/**
* 查询解密
* 依赖注解 SensitiveData 与 SensitiveField
* 需要解密的类:类上需要标记SensitiveData 敏感属性 包括集合 需要添加SensitiveField
* 解释: @Intercepts 注解开启拦截器,@Signature 注解定义拦截器的实际类型。
*
* @Signature中 type 属性指定当前拦截器使用StatementHandler 、ResultSetHandler、ParameterHandler,Executor的一种
* method 属性指定使用以上四种类型的具体方法(可进入class内部查看其方法)。
* args 属性指定预编译语句
*
* Interceptor 是Mybatis 的 拦截器接口
*/
@Intercepts({
@Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class})
})
public class EncryptInterceptor implements Interceptor {
private EncryptService encryptService;
public EncryptInterceptor(EncryptService encryptService) {
this.encryptService = encryptService;
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
encryptService.encryptSensitiveField(parameterHandler);
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
/**
* 这里就是将拦截器和对象包装在一起:
* ①获取拦截器的Intercepts注解的所有Signature参数,即该拦截器要拦截的类和对象的方法、参数
* ②获取拦截对象的类
* ③获取所有接口
* ④根据返回的接口数量,判断是否要拦截的,要拦截的对象生成将拦截器和拦截对象封装在一起的代理对象
*/
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
/**
* 在MyBatis配置文件中配置插件时可以设置参数,在setProperties函数中调用 Properties.getProperty("param1")
* 方法可以得到配置的值
*/
}
}
4.工具类
package com.xhc.study.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
@Slf4j
public class AESUtil {
private static final String KEY_ALGORITHM = "AES";
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算法
/**
* 敏感数据自定义前缀
*/
public static final String KEY_SENSITIVE = "W86z:";
/**
* AES 加密操作
*
* @param content 待加密内容
* @return 返回Base64转码后的加密数据
*/
public static String encrypt(String content, String key) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);// 创建密码器
byte[] byteContent = content.getBytes(StandardCharsets.UTF_8);
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));// 初始化为加密模式的密码器
byte[] result = cipher.doFinal(byteContent);// 加密
//Base64是一种基于64个可打印字符来表示二进制数据的表示方法。
return Base64Utils.encodeToString(result);//通过Base64转码返回
} catch (Exception ex) {
log.error(ex.getMessage());
}
return null;
}
/**
* AES 解密操作
*
* @param content
* @return
*/
public static String decrypt(String content, String key) {
try {
//实例化
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
//使用密钥初始化,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key));
//执行操作
//Base64是一种基于64个可打印字符来表示二进制数据的表示方法。
byte[] result = cipher.doFinal(Base64Utils.decodeFromString(content));
return new String(result, StandardCharsets.UTF_8);
} catch (Exception ex) {
log.error(ex.getMessage());
}
return null;
}
/**
* 生成加密秘钥
* @return
*/
private static Key getSecretKey(String key) throws NoSuchAlgorithmException {
//返回生成指定算法密钥生成器的 KeyGenerator 对象
KeyGenerator kg = null;
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.setSeed(key.getBytes());
try {
kg = KeyGenerator.getInstance(KEY_ALGORITHM);
//AES 要求密钥长度为 128
kg.init(128, random);
//生成一个密钥
SecretKey secretKey = kg.generateKey();
return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);// 转换为AES专用密钥
} catch (NoSuchAlgorithmException ex) {
log.error(ex.getMessage());
}
return null;
}
public static String encryptFieldStr(String splitWord, String key) {
String[] str = splitWord.split("|");
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < str.length; i++) {
stringBuffer.append(encrypt(str[i], key));
}
return stringBuffer.toString();
}
}
package com.xhc.study.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.springframework.util.Assert;
import org.wltea.analyzer.lucene.IKAnalyzer;
import java.io.StringReader;
@Slf4j
public class AnalyzerUtil {
public static String splitWord(String word) {
Assert.hasText(word, "需要进行拆分的字符串不能为空");
String resultStr = "";
Analyzer analyzer = new IKAnalyzer(true);
StringReader reader = new StringReader(word);
try {
TokenStream ts = analyzer.tokenStream("", reader);
CharTermAttribute term = ts.getAttribute(CharTermAttribute.class);
while (ts.incrementToken()) {
resultStr = resultStr + term.toString() + "|";
}
} catch (Exception e) {
log.error("分词出现错误:", e);
} finally {
reader.close();
return resultStr;
}
}
}
package com.xhc.study.util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
@Component
public class EncryptUtil {
@Value("${aes.key}")
private String aesKey;
/**
* 对分割后的字符串进行加密
*
* @param str
* @return
*/
public String splitEncryptStr(String str) {
Assert.hasText(str, "需要加密的字符串不能为空");
// 将字符串进行分词
String splitWord = AnalyzerUtil.splitWord(str);
// 分别将分词后的信息进行加密,然后拼接
String extendField = AESUtil.encryptFieldStr(splitWord, aesKey);
// 处理加密的扩展字段
return extendField;
}
}
package com.xhc.study.util;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.xhc.study.anno.EncryptField;
import com.xhc.study.anno.SensitiveData;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.CollectionUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class SensitiveUtil {
/**
* 判断一个类是否敏感类,即是否有注解 @SensitiveData
* @param clazz
* @return
*/
public static boolean isSensitiveClass(Class<?> clazz){
SensitiveData sensitiveData = AnnotationUtils.findAnnotation(clazz,SensitiveData.class);
return ObjectUtils.isNotEmpty(sensitiveData);
}
/**
* 获取敏感类里面的敏感字段
* @param clazz
* @return
*/
public static List<Field> getSensitiveField(Class<?> clazz){
List<Field> list = new ArrayList<>();
Field[] fields = ReflectUtil.getFields(clazz);
for (Field fs: fields) {
boolean annotationPresent = fs.isAnnotationPresent(EncryptField.class);
if(annotationPresent){
list.add(fs);
}
}
return list;
}
public static Field getFieldByName(String fieldName, Class<?> clazz){
Field[] fields = ReflectUtil.getFields(clazz);
List<Field> fieldList = Arrays.stream(fields).filter(field -> fieldName.equals(field.getName())).collect(Collectors.toList());
return CollectionUtils.isEmpty(fieldList) ? null : fieldList.get(0);
}
}
5.测试的实体对象
package com.xhc.study.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.xhc.study.anno.EncryptField;
import com.xhc.study.anno.SensitiveData;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("`order`")
@SensitiveData
public class Order implements Serializable {
/**
* 主键id
*/
@TableId(type = IdType.INPUT)
private Long id;
/**
* 用户姓名
*/
private String userName;
/**
* 用户手机号
*/
private String phone;
/**
* 用户收获地址
*/
@EncryptField(extendField = "addressExtendField")
private String address;
/**
* 扩展字段,该扩展字段用来存储被加密的信息经过分词后加密的结果
*/
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String addressExtendField;
}
5.需要注意:
// 需要对加密关联字段进行默认空字符填充,因为在执行insert时,首先会预编译sql,然后才会通过拦截器设置加密参数,如果预编译的sql中没有指定属性,加密参数也设置不上 order.setAddressExtendField("");
orderDao.insert(order);
四:结语
按照上述的流程即可完成基于mybatis插件实现加解密模糊查询的功能,以后使用只需要在需要的字段上使用注解即可。有什么疑问,可以在下方留言。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)