自己动手编写一个Mybatis插件:Mybatis脱敏插件
ztj100 2025-01-07 17:22 19 浏览 0 评论
1. 前言
在日常开发中,身份证号、手机号、卡号、客户号等个人信息都需要进行数据脱敏。否则容易造成个人隐私泄露,客户资料泄露,给不法分子可乘之机。但是数据脱敏不是把敏感信息隐藏起来,而是看起来像真的一样,实际上不能是真的。我以前的公司就因为不重视脱敏,一名员工在离职的时候通过后台的导出功能导出了核心的客户资料卖给了竞品,给公司造成了重大的损失。当然这里有数据管理的原因,但是脱敏仍旧是不可忽略的一环,脱敏可以从一定程度上保证数据的合规使用。下面就是一份经过脱敏的数据:
2. Mybatis 脱敏插件
最近在研究Mybatis的插件,所以考虑能不能在ORM中搞一搞脱敏,所以就尝试了一下,这里分享一下思路。借此也分享一下Mybatis插件开发的思路。
2.1 Mybatis 插件接口
Mybatis中使用插件,需要实现接口org.apache.ibatis.plugin.Interceptor,如下所示:
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
?
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
?
default void setProperties(Properties properties) {
// NOP
}
?
}
这里其实最核心的是Object intercept(Invocation invocation)方法,这是我们需要实现的方法。
2.2 Invocation 对象
那么核心方法中的Invocation是个什么概念呢?
public class Invocation {
?
private final Object target;
private final Method method;
private final Object[] args;
?
public Invocation(Object target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}
?
public Object getTarget() {
return target;
}
?
public Method getMethod() {
return method;
}
?
public Object[] getArgs() {
return args;
}
?
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
?
}
这个东西包含了四个概念:
- target 拦截的对象
- method 拦截target中的具体方法,也就是说Mybatis插件的粒度是精确到方法级别的。
- args 拦截到的参数。
- proceed 执行被拦截到的方法,你可以在执行的前后做一些事情。
2.3 拦截签名
既然我们知道了Mybatis插件的粒度是精确到方法级别的,那么疑问来了,插件如何知道轮到它工作了呢?
所以Mybatis设计了签名机制来解决这个问题,通过在插件接口上使用注解@Intercepts标注来解决这个问题。
@Intercepts(@Signature(type = ResultSetHandler.class,
method = "handleResultSets",
args = {Statement.class}))
就像上面一样,事实上就等于配置了一个Invocation。
2.4 插件的作用域
那么问题又来了,Mybatis插件能拦截哪些对象,或者说插件能在哪个生命周期阶段起作用呢?它可以拦截以下四大对象:
- Executor 是SQL执行器,包含了组装参数,组装结果集到返回值以及执行SQL的过程,粒度比较粗。
- StatementHandler 用来处理SQL的执行过程,我们可以在这里重写SQL非常常用。
- ParameterHandler 用来处理传入SQL的参数,我们可以重写参数的处理规则。
- ResultSetHandler 用于处理结果集,我们可以重写结果集的组装规则。
你需要做的就是明确的你的业务需要在上面四个对象的哪个处理阶段拦截处理即可。
2.5 MetaObject
Mybatis提供了一个工具类org.apache.ibatis.reflection.MetaObject。它通过反射来读取和修改一些重要对象的属性。我们可以利用它来处理四大对象的一些属性,这是Mybatis插件开发的一个常用工具类。
- Object getValue(String name) 根据名称获取对象的属性值,支持OGNL表达式。
- void setValue(String name, Object value) 设置某个属性的值。
- Class<?> getSetterType(String name) 获取setter方法的入参类型。
- Class<?> getGetterType(String name) 获取getter方法的返回值类型。
通常我们使用SystemMetaObject.forObject(Object object)来实例化MetaObject对象。你会在接下来的实战DEMO中看到我使用它。
3. Mybatis 脱敏插件实战
接下来我就把开头的脱敏需求实现一下。首先需要对脱敏字段进行标记并确定使用的脱敏策略。
编写脱敏函数:
/**
* 具体策略的函数
* @author felord.cn
* @since 11:24
**/
public interface Desensitizer extends Function<String,String> {
?
}
编写脱敏策略枚举:
/**
* 脱敏策略.
*
* @author felord.cn
* @since 11 :25
*/
public enum SensitiveStrategy {
/**
* Username sensitive strategy.
*/
USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
/**
* Id card sensitive type.
*/
ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1****$2")),
/**
* Phone sensitive type.
*/
PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
?
/**
* Address sensitive type.
*/
ADDRESS(s -> s.replaceAll("(\\S{8})\\S{4}(\\S*)\\S{4}", "$1****$2****"));
?
?
private final Desensitizer desensitizer;
?
SensitiveStrategy(Desensitizer desensitizer) {
this.desensitizer = desensitizer;
}
?
/**
* Gets desensitizer.
*
* @return the desensitizer
*/
public Desensitizer getDesensitizer() {
return desensitizer;
}
}
编写脱敏字段的标记注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sensitive {
SensitiveStrategy strategy();
}
我们的返回对象中如果某个字段需要脱敏,只需要通过标记就可以了。例如下面这样:
@Data
public class UserInfo {
?
private static final long serialVersionUID = -8938650956516110149L;
private Long userId;
@Sensitive(strategy = SensitiveStrategy.USERNAME)
private String name;
private Integer age;
}
然后就是编写插件了,我可以确定的是需要拦截的是ResultSetHandler对象的handleResultSets方法,我们只需要实现插件接口Interceptor并添加签名就可以了。全部逻辑如下:
@Slf4j
@Intercepts(@Signature(type = ResultSetHandler.class,
method = "handleResultSets",
args = {Statement.class}))
public class SensitivePlugin implements Interceptor {
@SuppressWarnings("unchecked")
@Override
public Object intercept(Invocation invocation) throws Throwable {
List<Object> records = (List<Object>) invocation.proceed();
// 对结果集脱敏
records.forEach(this::sensitive);
return records;
}
?
?
private void sensitive(Object source) {
// 拿到返回值类型
Class<?> sourceClass = source.getClass();
// 初始化返回值类型的 MetaObject
MetaObject metaObject = SystemMetaObject.forObject(source);
// 捕捉到属性上的标记注解 @Sensitive 并进行对应的脱敏处理
Stream.of(sourceClass.getDeclaredFields())
.filter(field -> field.isAnnotationPresent(Sensitive.class))
.forEach(field -> doSensitive(metaObject, field));
}
?
?
private void doSensitive(MetaObject metaObject, Field field) {
// 拿到属性名
String name = field.getName();
// 获取属性值
Object value = metaObject.getValue(name);
// 只有字符串类型才能脱敏 而且不能为null
if (String.class == metaObject.getGetterType(name) && value != null) {
Sensitive annotation = field.getAnnotation(Sensitive.class);
// 获取对应的脱敏策略 并进行脱敏
SensitiveStrategy type = annotation.strategy();
Object o = type.getDesensitizer().apply((String) value);
// 把脱敏后的值塞回去
metaObject.setValue(name, o);
}
}
}
然后配置脱敏插件使之生效:
@Bean
public SensitivePlugin sensitivePlugin(){
return new SensitivePlugin();
}
操作查询获得结果 UserInfo(userId=123123, name=李*龙, age=28) ,成功将指定字段进行了脱敏。
补充一句,其实脱敏也可以在JSON序列化的时候进行。
4. 总结
今天对编写Mybatis插件的一些要点进行了说明,同时根据说明实现了一个脱敏插件。但是请注意一定要熟悉四大对象的生命周期,否则自写插件可能会造成意想不到的结果。本次DEMO的插件可从个人GitHub获取。如果你觉得有用请无情的点赞、转发。
相关推荐
- 其实TensorFlow真的很水无非就这30篇熬夜练
-
好的!以下是TensorFlow需要掌握的核心内容,用列表形式呈现,简洁清晰(含表情符号,<300字):1.基础概念与环境TensorFlow架构(计算图、会话->EagerE...
- 交叉验证和超参数调整:如何优化你的机器学习模型
-
准确预测Fitbit的睡眠得分在本文的前两部分中,我获取了Fitbit的睡眠数据并对其进行预处理,将这些数据分为训练集、验证集和测试集,除此之外,我还训练了三种不同的机器学习模型并比较了它们的性能。在...
- 机器学习交叉验证全指南:原理、类型与实战技巧
-
机器学习模型常常需要大量数据,但它们如何与实时新数据协同工作也同样关键。交叉验证是一种通过将数据集分成若干部分、在部分数据上训练模型、在其余数据上测试模型的方法,用来检验模型的表现。这有助于发现过拟合...
- 深度学习中的类别激活热图可视化
-
作者:ValentinaAlto编译:ronghuaiyang导读使用Keras实现图像分类中的激活热图的可视化,帮助更有针对性...
- 超强,必会的机器学习评估指标
-
大侠幸会,在下全网同名[算法金]0基础转AI上岸,多个算法赛Top[日更万日,让更多人享受智能乐趣]构建机器学习模型的关键步骤是检查其性能,这是通过使用验证指标来完成的。选择正确的验证指...
- 机器学习入门教程-第六课:监督学习与非监督学习
-
1.回顾与引入上节课我们谈到了机器学习的一些实战技巧,比如如何处理数据、选择模型以及调整参数。今天,我们将更深入地探讨机器学习的两大类:监督学习和非监督学习。2.监督学习监督学习就像是有老师的教学...
- Python 模型部署不用愁!容器化实战,5 分钟搞定环境配置
-
你是不是也遇到过这种糟心事:花了好几天训练出的Python模型,在自己电脑上跑得顺顺当当,一放到服务器就各种报错。要么是Python版本不对,要么是依赖库冲突,折腾半天还是用不了。别再喊“我...
- 神经网络与传统统计方法的简单对比
-
传统的统计方法如...
- 自回归滞后模型进行多变量时间序列预测
-
下图显示了关于不同类型葡萄酒销量的月度多元时间序列。每种葡萄酒类型都是时间序列中的一个变量。假设要预测其中一个变量。比如,sparklingwine。如何建立一个模型来进行预测呢?一种常见的方...
- 苹果AI策略:慢哲学——科技行业的“长期主义”试金石
-
苹果AI策略的深度原创分析,结合技术伦理、商业逻辑与行业博弈,揭示其“慢哲学”背后的战略智慧:一、反常之举:AI狂潮中的“逆行者”当科技巨头深陷AI军备竞赛,苹果的克制显得格格不入:功能延期:App...
- 时间序列预测全攻略,6大模型代码实操
-
如果你对数据分析感兴趣,希望学习更多的方法论,希望听听经验分享,欢迎移步宝藏公众号...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)