一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - 自己動手編寫一個Mybatis插件之Mybatis脫敏插件

自己動手編寫一個Mybatis插件之Mybatis脫敏插件

2020-08-11 18:07碼農(nóng)小胖哥 Java教程

這篇文章主要介紹了自己動手編寫一個Mybatis插件之Mybatis脫敏插件,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

自己動手編寫一個Mybatis插件之Mybatis脫敏插件

1. 前言

在日常開發(fā)中,身份證號、手機(jī)號、卡號、客戶號等個人信息都需要進(jìn)行數(shù)據(jù)脫敏。否則容易造成個人隱私泄露,客戶資料泄露,給不法分子可乘之機(jī)。但是數(shù)據(jù)脫敏不是把敏感信息隱藏起來,而是看起來像真的一樣,實際上不能是真的。我以前的公司就因為不重視脫敏,一名員工在離職的時候通過后臺的導(dǎo)出功能導(dǎo)出了核心的客戶資料賣給了競品,給公司造成了重大的損失。當(dāng)然這里有數(shù)據(jù)管理的原因,但是脫敏仍舊是不可忽略的一環(huán),脫敏可以從一定程度上保證數(shù)據(jù)的合規(guī)使用。下面就是一份經(jīng)過脫敏的數(shù)據(jù):

自己動手編寫一個Mybatis插件之Mybatis脫敏插件

2. Mybatis 脫敏插件

最近在研究Mybatis的插件,所以考慮能不能在ORM中搞一搞脫敏,所以就嘗試了一下,這里分享一下思路。借此也分享一下Mybatis插件開發(fā)的思路。

2.1 Mybatis 插件接口

Mybatis中使用插件,需要實現(xiàn)接口org.apache.ibatis.plugin.Interceptor,如下所示:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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)方法,這是我們需要實現(xiàn)的方法。

2.2 Invocation 對象

那么核心方法中的Invocation 是個什么概念呢?

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 攔截到的參數(shù)。
  • proceed 執(zhí)行被攔截到的方法,你可以在執(zhí)行的前后做一些事情。

2.3 攔截簽名

既然我們知道了Mybatis插件的粒度是精確到方法級別的,那么疑問來了,插件如何知道輪到它工作了呢?

所以Mybatis設(shè)計了簽名機(jī)制來解決這個問題,通過在插件接口上使用注解@Intercepts標(biāo)注來解決這個問題。

?
1
2
3
@Intercepts(@Signature(type = ResultSetHandler.class,
  method = "handleResultSets",
  args = {Statement.class})

就像上面一樣,事實上就等于配置了一個Invocation

2.4 插件的作用域

那么問題又來了,Mybatis插件能攔截哪些對象,或者說插件能在哪個生命周期階段起作用呢?它可以攔截以下四大對象:

  • Executor 是SQL執(zhí)行器,包含了組裝參數(shù),組裝結(jié)果集到返回值以及執(zhí)行SQL的過程,粒度比較粗。
  • StatementHandler 用來處理SQL的執(zhí)行過程,我們可以在這里重寫SQL非常常用。
  • ParameterHandler 用來處理傳入SQL的參數(shù),我們可以重寫參數(shù)的處理規(guī)則。
  • ResultSetHandler 用于處理結(jié)果集,我們可以重寫結(jié)果集的組裝規(guī)則。

你需要做的就是明確的你的業(yè)務(wù)需要在上面四個對象的哪個處理階段攔截處理即可。

2.5 MetaObject

Mybatis提供了一個工具類org.apache.ibatis.reflection.MetaObject。它通過反射來讀取和修改一些重要對象的屬性。我們可以利用它來處理四大對象的一些屬性,這是Mybatis插件開發(fā)的一個常用工具類。

  • Object getValue(String name) 根據(jù)名稱獲取對象的屬性值,支持OGNL表達(dá)式。
  • void setValue(String name, Object value) 設(shè)置某個屬性的值。
  • Class<?> getSetterType(String name) 獲取setter方法的入?yún)㈩愋汀?/li>
  • Class<?> getGetterType(String name) 獲取getter方法的返回值類型。

通常我們使用SystemMetaObject.forObject(Object object)來實例化MetaObject對象。你會在接下來的實戰(zhàn)DEMO中看到我使用它。

3. Mybatis 脫敏插件實戰(zhàn)

接下來我就把開頭的脫敏需求實現(xiàn)一下。首先需要對脫敏字段進(jìn)行標(biāo)記并確定使用的脫敏策略。

編寫脫敏函數(shù):

?
1
2
3
4
5
6
7
8
/**
 * 具體策略的函數(shù)
 * @author felord.cn
 * @since 11:24
 **/
public interface Desensitizer extends Function<String,String> {
 
}

編寫脫敏策略枚舉:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
 * 脫敏策略.
 *
 * @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;
 }
}

編寫脫敏字段的標(biāo)記注解:

?
1
2
3
4
5
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sensitive {
 SensitiveStrategy strategy();
}

我們的返回對象中如果某個字段需要脫敏,只需要通過標(biāo)記就可以了。例如下面這樣:

?
1
2
3
4
5
6
7
8
9
@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方法,我們只需要實現(xiàn)插件接口Interceptor并添加簽名就可以了。全部邏輯如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@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();
  // 對結(jié)果集脫敏
  records.forEach(this::sensitive);
  return records;
 }
 
 
 private void sensitive(Object source) {
  // 拿到返回值類型
  Class<?> sourceClass = source.getClass();
  // 初始化返回值類型的 MetaObject
  MetaObject metaObject = SystemMetaObject.forObject(source);
  // 捕捉到屬性上的標(biāo)記注解 @Sensitive 并進(jìn)行對應(yīng)的脫敏處理
  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);
   // 獲取對應(yīng)的脫敏策略 并進(jìn)行脫敏
   SensitiveStrategy type = annotation.strategy();
   Object o = type.getDesensitizer().apply((String) value);
   // 把脫敏后的值塞回去
   metaObject.setValue(name, o);
  }
 }
}

然后配置脫敏插件使之生效:

?
1
2
3
4
@Bean
public SensitivePlugin sensitivePlugin(){
 return new SensitivePlugin();
}

操作查詢獲得結(jié)果 UserInfo(userId=123123, name=李*龍, age=28) ,成功將指定字段進(jìn)行了脫敏。

補充一句,其實脫敏也可以在JSON序列化的時候進(jìn)行。

4. 總結(jié)

今天對編寫Mybatis插件的一些要點進(jìn)行了說明,同時根據(jù)說明實現(xiàn)了一個脫敏插件。但是請注意一定要熟悉四大對象的生命周期,否則自寫插件可能會造成意想不到的結(jié)果。

到此這篇關(guān)于自己動手編寫一個Mybatis插件之Mybatis脫敏插件的文章就介紹到這了,更多相關(guān)Mybatis脫敏插件內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://www.cnblogs.com/felordcn/p/13473844.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 男人天堂网在线 | 91精品天美精东蜜桃传媒免费 | 免费一级特黄特色大片在线观看 | 日本高清中文字幕 | 免费看全黄特黄毛片 | 小舞丝袜调教喷水沦为肉奴 | 黑人巨茎大战欧美白妇 | 国产欧美日韩在线观看精品 | 嫩草视频在线观看视频播放 | 欧美日本一道高清免费3区 欧美人做人爱a全程免费 | 91日本 | 国产成人综合亚洲一区 | 99热成人精品热久久669 | a毛片免费观看完整 | asianfemdom冷柔女王 | 红色一片在线影视 | 久久成人国产精品一区二区 | 美女和男生搞基 | 国产伊人久久 | 九九久久国产 | 小早川怜子息梦精在线播放 | 色碰视频 | 国色天香社区视频在线观看免费完整版 | 学生小泬无遮挡女HD | 九九艹 | 天天综合天天影视色香欲俱全 | 亚洲国产高清视频 | 99国产精品热久久久久久夜夜嗨 | 99r8这里精品热视频免费看 | 国产一区二区不卡视频 | 好大好爽好硬我要喷水了 | 波多野结衣中文字幕乱七八糟 | 美女扒开尿口让男生添 漫画 | 国产rpg迷雾之风冷狐破解 | 午夜爱爱爱爱爽爽爽视频网站 | 四虎影在线永久免费观看 | 91社区在线观看精品 | 亚洲欧美日韩中文高清一 | gayrb免费漫画入口 | 操大肥b| 久久青青草原 |