最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于MyBatis插件實(shí)現(xiàn)字段加解密的實(shí)現(xiàn)示例

 更新時(shí)間:2025年11月18日 09:26:23   作者:Wh1te  
本文主要介紹了基于MyBatis插件實(shí)現(xiàn)字段加解密的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

對(duì)于大多數(shù)系統(tǒng)來(lái)說(shuō),敏感數(shù)據(jù)的加密存儲(chǔ)都是必須考慮和實(shí)現(xiàn)的。最近在公司的項(xiàng)目中也接到了相關(guān)的安全需求,因?yàn)轫?xiàng)目使用了 MyBatis 作為數(shù)據(jù)庫(kù)持久層框架,在經(jīng)過(guò)一番調(diào)研后決定使用其插件機(jī)制來(lái)實(shí)現(xiàn)字段加解密功能,并且封裝成一個(gè)輕量級(jí)、支持配置、方便擴(kuò)展的組件提供給其他項(xiàng)目使用。

MyBatis 的插件機(jī)制

簡(jiǎn)介

MyBatis 提供了插件功能,它允許你攔截 MyBatis 執(zhí)行過(guò)程中的某個(gè)方法,對(duì)其增加自定義操作。默認(rèn)情況下,MyBatis 允許攔截的方法包括:

類(lèi)方法說(shuō)明
org.apache.ibatis.executor.Executorupdate, query, flushStatements, commit, rollback, getTransaction, close, isClosed攔截執(zhí)行器的方法
org.apache.ibatis.executor.parameter.ParameterHandlergetParameterObject, setParameters說(shuō)明:攔截參數(shù)處理的方法
org.apache.ibatis.executor.resultset.ResultSetHandlerhandleResultSets, handleOutputParameters攔截結(jié)果集處理的方法
org.apache.ibatis.executor.statement.StatementHandlerprepare, parameterize, batch, update, query攔截 Sql 語(yǔ)句構(gòu)建的方法

插件實(shí)現(xiàn)

在 MyBatis 中,一個(gè)插件其實(shí)就是一個(gè)攔截器,插件的實(shí)現(xiàn)方式非常簡(jiǎn)單,只需要實(shí)現(xiàn) org.apache.ibatis.plugin.Interceptor 接口,并且通過(guò) @Intercepts 注解指定要攔截的方法簽名即可。 以下是官方文檔提供的例子:

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  private Properties properties = new Properties();

  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    // implement pre-processing if needed
    Object returnObject = invocation.proceed();
    // implement post-processing if needed
    return returnObject;
  }

  @Override
  public void setProperties(Properties properties) {
    this.properties = properties;
  }
}

上面的插件會(huì)攔截 org.apache.ibatis.executor.Executor#update 方法的所有調(diào)用,你可以在 invocation.proceed() 前后增加插件邏輯。

字段加解密實(shí)現(xiàn)

對(duì)于字段加解密來(lái)說(shuō),需要關(guān)注的點(diǎn)就是查詢、插入和更新,在進(jìn)行這些操作的時(shí)候需要對(duì)字段進(jìn)行處理(插入、更新時(shí)加密,查詢時(shí)解密),與上文提到的攔截點(diǎn)的對(duì)應(yīng)關(guān)系如下:

  • 插入、更新:org.apache.ibatis.executor.Executor#update
  • 查詢:org.apache.ibatis.executor.resultset.ResultSetHandler#handleResultSets

代碼實(shí)現(xiàn)

先上完整代碼:github.com/WhiteDG/myb…

一般場(chǎng)景下只有包含敏感數(shù)據(jù)的字段才需要進(jìn)行加解密,所以需要一個(gè)注解來(lái)標(biāo)記哪些字段需要加解密,這里定義為 @EncryptedField,提供兩個(gè)屬性:

  • key:加解密時(shí)用到的密鑰
  • encryptor:指定加解密器,不指定則使用全局的加解密器
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptedField {

    String key() default "";

    Class<? extends IEncryptor> encryptor() default IEncryptor.class;
}

加密的整體思路就是通過(guò) Invocation 拿到方法參數(shù),有兩種情況:一種是實(shí)體類(lèi),一種是 ParamMap,實(shí)體類(lèi)通過(guò)注解確定需要加密的字段,ParamMap 通過(guò)配置的參數(shù)名前綴確定需要加密的字段,然后使用加密器對(duì)需要加密的字段進(jìn)行加密覆蓋掉原始值即可。如果是實(shí)體類(lèi)則在方法執(zhí)行完成后還需要對(duì) key 字段進(jìn)行回寫(xiě)處理。 在對(duì)參數(shù)進(jìn)行處理前使用 Kryo 拷貝了一份源數(shù)據(jù),目的是保留方法調(diào)用時(shí)的原始參數(shù),避免經(jīng)過(guò)插件的 SQL 執(zhí)行完成后,原始參數(shù)變成已加密的。

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        if (Util.encryptionRequired(parameter, ms.getSqlCommandType())) {
            Kryo kryo = null;
            try {
                kryo = KryoPool.obtain();
                Object copiedParameter = kryo.copy(parameter);
                boolean isParamMap = parameter instanceof MapperMethod.ParamMap;
                if (isParamMap) {
                    //noinspection unchecked
                    MapperMethod.ParamMap<Object> paramMap = (MapperMethod.ParamMap<Object>) copiedParameter;
                    encryptParamMap(paramMap);
                } else {
                    encryptEntity(copiedParameter);
                }
                args[1] = copiedParameter;
                Object result = invocation.proceed();
                if (!isParamMap) {
                    handleKeyProperties(ms, parameter, copiedParameter);
                }
                return result;
            } finally {
                if (kryo != null) {
                    KryoPool.free(kryo);
                }
            }
        } else {
            return invocation.proceed();
        }
    }

解密插件與加密插件類(lèi)似,通過(guò) Invocation 拿到查詢 SQL 執(zhí)行后返回的結(jié)果集,有兩種情況:一種是返回 ArrayList,一種是返回單個(gè)實(shí)體,同樣通過(guò)注解確定需要解密的字段對(duì)其進(jìn)行解密然后覆蓋掉原始加密的值。

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result = invocation.proceed();
        if (result == null) {
            return null;
        }
        if (result instanceof ArrayList) {
            //noinspection rawtypes
            ArrayList resultList = (ArrayList) result;
            if (resultList.isEmpty()) {
                return result;
            }
            Object firstItem = resultList.get(0);
            boolean needToDecrypt = Util.decryptionRequired(firstItem);
            if (!needToDecrypt) {
                return result;
            }
            Set<Field> encryptedFields = EncryptedFieldsProvider.get(firstItem.getClass());
            if (encryptedFields == null || encryptedFields.isEmpty()) {
                return result;
            }
            for (Object item : resultList) {
                decryptEntity(encryptedFields, item);
            }
        } else {
            if (Util.decryptionRequired(result)) {
                decryptEntity(EncryptedFieldsProvider.get(result.getClass()), result);
            }
        }
        return result;
    }

支持配置、方便擴(kuò)展的實(shí)現(xiàn)

作為一個(gè)通用的組件(這里命名為 mybatis-crypto),支持配置和方便擴(kuò)展是基本的要求。

mybatis-crypto 提供了以下幾個(gè)配置項(xiàng)滿足基本使用:

配置項(xiàng)說(shuō)明默認(rèn)值
mybatis-crypto.enabled是否啟用 mybatis-cryptotrue
mybatis-crypto.fail-fast快速失敗,加解密過(guò)程中發(fā)生異常是否中斷。true:拋出異常,false:使用原始值,打印 warn 級(jí)別日志true
mybatis-crypto.mapped-key-prefixes@Param 參數(shù)名的前綴,前綴匹配則會(huì)進(jìn)行加密處理
mybatis-crypto.default-encryptor全局默認(rèn) Encryptor
mybatis-crypto.default-key全局默認(rèn) Encryptor 的密鑰

mybatis-crypto 核心包默認(rèn)不提供具體的加解密方法,開(kāi)發(fā)者可以通過(guò)引入 mybatis-crypto-encryptors 使用其提供的常用加解密類(lèi),或者實(shí)現(xiàn) io.github.whitedg.mybatis.crypto.IEncryptor 自行擴(kuò)展加解密方法。

Starter 封裝

目前大多數(shù)項(xiàng)目都是基于 spring-boot 進(jìn)行開(kāi)發(fā)的,所以將 mybatis-crypto 封裝成一個(gè) starter 會(huì)更方便開(kāi)發(fā)者使用。starter 的主要工作就是讀取配置,自動(dòng)裝配,因此它的實(shí)現(xiàn)非常簡(jiǎn)單,只有兩個(gè)類(lèi),MybatisCryptoProperties 獲取配置,MyBatisCryptoAutoConfiguration 加載插件。

@Configuration
@ConditionalOnProperty(value = "mybatis-crypto.enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(MybatisCryptoProperties.class)
public class MyBatisCryptoAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(MybatisEncryptionPlugin.class)
    public MybatisEncryptionPlugin encryptionInterceptor(MybatisCryptoProperties properties) {
        return new MybatisEncryptionPlugin(properties.toMybatisCryptoConfig());
    }

    @Bean
    @ConditionalOnMissingBean(MybatisDecryptionPlugin.class)
    public MybatisDecryptionPlugin decryptionInterceptor(MybatisCryptoProperties properties) {
        return new MybatisDecryptionPlugin(properties.toMybatisCryptoConfig());
    }

}

總結(jié)

本文簡(jiǎn)單介紹了基于 mybatis 插件機(jī)制實(shí)現(xiàn)字段加解密的思路及流程,并將其封裝成一個(gè)通用的 spring-boot-starter 組件,開(kāi)發(fā)者可以方便的引入使用,同時(shí)也提供了加解密方法集合 mybatis-crypto-encryptors

組件的具體使用方法和示https://github.com/WhiteDG/mybatis-crypto

到此這篇關(guān)于基于MyBatis插件實(shí)現(xiàn)字段加解密的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)MyBatis 字段加解密內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中ConstraintValidator接口使用方法詳解

    Java中ConstraintValidator接口使用方法詳解

    在現(xiàn)代 Java 應(yīng)用開(kāi)發(fā)中,數(shù)據(jù)校驗(yàn)是保證系統(tǒng)健壯性和數(shù)據(jù)一致性的核心環(huán)節(jié),無(wú)論是 Web 請(qǐng)求參數(shù)、數(shù)據(jù)庫(kù)實(shí)體對(duì)象,還是業(yè)務(wù)邏輯中的中間狀態(tài),都需要通過(guò)嚴(yán)格的校驗(yàn)規(guī)則來(lái)確保數(shù)據(jù)的合法性,Java 提供了 Bean Validation,本文給大家介紹了ConstraintValidator接口使用方法
    2025-07-07
  • Mybatis自定義TypeHandler解決特殊類(lèi)型轉(zhuǎn)換問(wèn)題詳解

    Mybatis自定義TypeHandler解決特殊類(lèi)型轉(zhuǎn)換問(wèn)題詳解

    這篇文章主要介紹了Mybatis自定義TypeHandler解決特殊類(lèi)型轉(zhuǎn)換問(wèn)題詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • j2ee之AJAX二級(jí)聯(lián)動(dòng)效果

    j2ee之AJAX二級(jí)聯(lián)動(dòng)效果

    這篇文章主要為大家詳細(xì)介紹了j2ee之AJAX二級(jí)聯(lián)動(dòng)效果的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • 詳解springboot配置多個(gè)redis連接

    詳解springboot配置多個(gè)redis連接

    Spring Boot為Redis, MongoDB, Elasticsearch, Solr和Gemfire提供自動(dòng)配置。本文詳細(xì)介紹了springboot配置多個(gè)redis連接,有興趣的可以了解一下。
    2017-04-04
  • Java實(shí)現(xiàn)矩陣加減乘除及轉(zhuǎn)制等運(yùn)算功能示例

    Java實(shí)現(xiàn)矩陣加減乘除及轉(zhuǎn)制等運(yùn)算功能示例

    這篇文章主要介紹了Java實(shí)現(xiàn)矩陣加減乘除及轉(zhuǎn)制等運(yùn)算功能,結(jié)合實(shí)例形式總結(jié)分析了java常見(jiàn)的矩陣運(yùn)算實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2018-01-01
  • 關(guān)于ArrayList初始化容量的問(wèn)題

    關(guān)于ArrayList初始化容量的問(wèn)題

    這篇文章主要介紹了關(guān)于ArrayList初始化容量的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Maven Profile多環(huán)境構(gòu)建實(shí)戰(zhàn)指南

    Maven Profile多環(huán)境構(gòu)建實(shí)戰(zhàn)指南

    在現(xiàn)代企業(yè)級(jí)Java項(xiàng)目開(kāi)發(fā)中,面對(duì)開(kāi)發(fā)(dev)、測(cè)試(test)、預(yù)發(fā)布(staging)、生產(chǎn)(prod)等多套環(huán)境的差異化配置管理,本文將從實(shí)戰(zhàn)角度深入剖析Maven Profile的多環(huán)境構(gòu)建體系,需要的朋友可以參考下
    2025-05-05
  • 淺談Java ThreadPoolExecutor的使用

    淺談Java ThreadPoolExecutor的使用

    今天我們來(lái)學(xué)習(xí)一下Java ThreadPoolExecutor的使用,文中有非常詳細(xì)的介紹及代碼示例,對(duì)正在學(xué)習(xí)Java的小伙伴們有很大的幫助,需要的朋友可以參考下
    2021-05-05
  • Java后臺(tái)定時(shí)器導(dǎo)致系統(tǒng)崩潰的原因和解決方法

    Java后臺(tái)定時(shí)器導(dǎo)致系統(tǒng)崩潰的原因和解決方法

    這篇文章主要介紹了Java后臺(tái)定時(shí)器導(dǎo)致系統(tǒng)崩潰的原因和解決方法,主要涵蓋了常見(jiàn)定時(shí)器類(lèi)型、系統(tǒng)崩潰的常見(jiàn)根因、真場(chǎng)景總結(jié)以及具體案例,需要的朋友可以參考下
    2025-11-11
  • java實(shí)現(xiàn)文件分片上傳并且斷點(diǎn)續(xù)傳的示例代碼

    java實(shí)現(xiàn)文件分片上傳并且斷點(diǎn)續(xù)傳的示例代碼

    本文主要介紹了java實(shí)現(xiàn)文件分片上傳并且斷點(diǎn)續(xù)傳的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05

最新評(píng)論

孟村| 长寿区| 香格里拉县| 玉田县| 锦州市| 天镇县| 崇阳县| 米林县| 鄂州市| 城步| 白玉县| 泾源县| 景东| 靖远县| 舟山市| 荥阳市| 宜昌市| 天门市| 庄河市| 博客| 黔西| 米林县| 桂平市| 廉江市| 安丘市| 乌兰浩特市| 宁乡县| 嫩江县| 大足县| 长岭县| 静宁县| 静安区| 台东市| 南平市| 来宾市| 广昌县| 湄潭县| 三河市| 樟树市| 钟山县| 云龙县|