1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        自定義注解+攔截器優(yōu)雅的實現(xiàn)敏感數(shù)據(jù)的加解密!

        共 9360字,需瀏覽 19分鐘

         ·

        2022-03-03 01:34

        程序員的成長之路
        互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
        關(guān)注

        閱讀本文大概需要 5.5 分鐘。
        來自:blog.csdn.net/bbcckkl/article/details/104069487
        在實際生產(chǎn)項目中,經(jīng)常需要對如身份證信息、手機號、真實姓名等的敏感數(shù)據(jù)進行加密數(shù)據(jù)庫存儲,但在業(yè)務代碼中對敏感信息進行手動加解密則十分不優(yōu)雅,甚至會存在錯加密、漏加密、業(yè)務人員需要知道實際的加密規(guī)則等的情況。
        本文將介紹使用springboot+mybatis攔截器+自定義注解的形式對敏感數(shù)據(jù)進行存儲前攔截加密的詳細過程。

        一、什么是Mybatis Plugin

        在mybatis官方文檔中,對于Mybatis plugin的的介紹是這樣的:
        MyBatis 允許你在已映射語句執(zhí)行過程中的某一點進行攔截調(diào)用。默認情況下,MyBatis 允許使用插件來攔截的方法調(diào)用包括:
        //語句執(zhí)行攔截??
        Executor?(update,?query,?flushStatements,?commit,?rollback,?getTransaction,?close,?isClosed)??
        ??
        //?參數(shù)獲取、設置時進行攔截??
        ParameterHandler?(getParameterObject,?setParameters)??
        ??
        //?對返回結(jié)果進行攔截??
        ResultSetHandler?(handleResultSets,?handleOutputParameters)??
        ??
        //sql語句攔截??
        StatementHandler?(prepare,?parameterize,?batch,?update,?query)??
        簡而言之,即在執(zhí)行sql的整個周期中,我們可以任意切入到某一點對sql的參數(shù)、sql執(zhí)行結(jié)果集、sql語句本身等進行切面處理?;谶@個特性,我們便可以使用其對我們需要進行加密的數(shù)據(jù)進行切面統(tǒng)一加密處理了(分頁插件 pageHelper 就是這樣實現(xiàn)數(shù)據(jù)庫分頁查詢的)。

        二、實現(xiàn)基于注解的敏感信息加解密攔截器

        2.1 實現(xiàn)思路

        對于數(shù)據(jù)的加密與解密,應當存在兩個攔截器對數(shù)據(jù)進行攔截操作
        參照官方文檔,因此此處我們應當使用ParameterHandler攔截器對入?yún)⑦M行加密
        使用ResultSetHandler攔截器對出參進行解密操作。
        圖片
        目標需要加密、解密的字段可能需要靈活變更,此時我們定義一個注解,對需要加密的字段進行注解,那么便可以配合攔截器對需要的數(shù)據(jù)進行加密與解密操作了。
        mybatis的interceptor接口有以下方法需要實現(xiàn)。
        public?interface?Interceptor?{??
        ???
        ??//主要參數(shù)攔截方法??
        ??Object?intercept(Invocation?invocation)?throws?Throwable;??
        ???
        ??//mybatis插件鏈??
        ??default?Object?plugin(Object?target)?{return?Plugin.wrap(target,?this);}??
        ???
        ??//自定義插件配置文件方法??
        ??default?void?setProperties(Properties?properties)?{}??
        ???
        }??

        2.2 定義需要加密解密的敏感信息注解

        定義注解敏感信息類(如實體類POJO\PO)的注解
        /**??
        ?*?注解敏感信息類的注解??
        ?*/
        ??
        @Inherited??
        @Target({?ElementType.TYPE?})??
        @Retention(RetentionPolicy.RUNTIME)??
        public?@interface?SensitiveData?{??
        }??
        定義注解敏感信息類中敏感字段的注解
        /**??
        ?*?注解敏感信息類中敏感字段的注解??
        ?*/
        ??
        @Inherited??
        @Target({?ElementType.Field?})??
        @Retention(RetentionPolicy.RUNTIME)??
        public?@interface?SensitiveField?{??
        }??

        2.3 定義加密接口及其實現(xiàn)類

        定義加密接口,方便以后拓展加密方法(如AES加密算法拓展支持PBE算法,只需要注入時指定一下便可)
        public?interface?EncryptUtil?{??
        ??????
        ????/**??
        ?????*?加密??
        ?????*??
        ?????*?@param?declaredFields?paramsObject所聲明的字段??
        ?????*?@param?paramsObject???mapper中paramsType的實例??
        ?????*?@return?T??
        ?????*?@throws?IllegalAccessException?字段不可訪問異常??
        ?????*/
        ??
        ??????T?encrypt(Field[]?declaredFields,?T?paramsObject)?throws?IllegalAccessException;??
        }??
        EncryptUtil 的AES加密實現(xiàn)類,此處AESUtil為自封裝的AES加密工具,需要的小伙伴可以自行封裝,本文不提供。
        @Component??
        public?class?AESEncrypt?implements?EncryptUtil?{??
        ??????
        ????@Autowired??
        ????AESUtil?aesUtil;??
        ???
        ????/**??
        ?????*?加密??
        ?????*??
        ?????*?@param?declaredFields?paramsObject所聲明的字段??
        ?????*?@param?paramsObject???mapper中paramsType的實例??
        ?????*?@return?T??
        ?????*?@throws?IllegalAccessException?字段不可訪問異常??
        ?????*/
        ??
        ????@Override??
        ????public??T?encrypt(Field[]?declaredFields,?T?paramsObject)?throws?IllegalAccessException?{??
        ????????for?(Field?field?:?declaredFields)?{??
        ????????????//取出所有被EncryptDecryptField注解的字段??
        ????????????SensitiveField?sensitiveField?=?field.getAnnotation(SensitiveField.class);??
        ????????????if?(!Objects.isNull(sensitiveField))?{??
        ????????????????field.setAccessible(true);??
        ????????????????Object?object?=?field.get(paramsObject);??
        ????????????????//暫時只實現(xiàn)String類型的加密??
        ????????????????if?(object?instanceof?String)?{??
        ????????????????????String?value?=?(String)?object;??
        ????????????????????//加密??這里我使用自定義的AES加密工具??
        ????????????????????field.set(paramsObject,?aesUtil.encrypt(value));??
        ????????????????}??
        ????????????}??
        ????????}??
        ????????return?paramsObject;??
        ????}??
        }??

        2.4 實現(xiàn)入?yún)⒓用軘r截器

        Myabtis包中的org.apache.ibatis.plugin.Interceptor攔截器接口要求我們實現(xiàn)以下三個方法
        public?interface?Interceptor?{??
        ???
        ??//核心攔截邏輯??
        ??Object?intercept(Invocation?invocation)?throws?Throwable;??
        ????
        ??//攔截器鏈??
        ??default?Object?plugin(Object?target)?{return?Plugin.wrap(target,?this);}??
        ???
        ??//自定義配置文件操作??
        ??default?void?setProperties(Properties?properties)?{?}??
        ???
        }??
        因此,參考官方文檔的示例,我們自定義一個入?yún)⒓用軘r截器。
        @Intercepts?注解開啟攔截器,@Signature?注解定義攔截器的實際類型。
        @Signature中
        • type 屬性指定當前攔截器使用StatementHandler 、ResultSetHandler、ParameterHandler,Executor的一種

        • method 屬性指定使用以上四種類型的具體方法(可進入class內(nèi)部查看其方法)。

        • args 屬性指定預編譯語句

        此處我們使用了?ParameterHandler.setParamters()方法,攔截mapper.xml中paramsType的實例(即在每個含有paramsType屬性mapper語句中,都執(zhí)行該攔截器,對paramsType的實例進行攔截處理)
        /**??
        ?*?加密攔截器??
        ?*?注意@Component注解一定要加上??
        ?*??
        ?*?@author?:?tanzj??
        ?*?@date?:?2020/1/19.??
        ?*/
        ??
        @Slf4j??
        @Component??
        @Intercepts({??
        ????????@Signature(type?=?ParameterHandler.class,?method?=?"setParameters",?args?=?PreparedStatement.class),??
        })??
        public?class?EncryptInterceptor?implements?Interceptor?
        {??
        ???
        ????private?final?EncryptDecryptUtil?encryptUtil;??
        ???
        ????@Autowired??
        ????public?EncryptInterceptor(EncryptDecryptUtil?encryptUtil)?{??
        ????????this.encryptUtil?=?encryptUtil;??
        ????}??
        ???
        ????@Override??
        ?????
        ????@Override??
        ????public?Object?intercept(Invocation?invocation)?throws?Throwable?{??
        ????????//@Signature?指定了?type=?parameterHandler?后,這里的?invocation.getTarget()?便是parameterHandler???
        ????????//若指定ResultSetHandler?,這里則能強轉(zhuǎn)為ResultSetHandler??
        ????????ParameterHandler?parameterHandler?=?(ParameterHandler)?invocation.getTarget();??
        ????????//?獲取參數(shù)對像,即?mapper?中?paramsType?的實例??
        ????????Field?parameterField?=?parameterHandler.getClass().getDeclaredField("parameterObject");??
        ????????parameterField.setAccessible(true);??
        ????????//取出實例??
        ????????Object?parameterObject?=?parameterField.get(parameterHandler);??
        ????????if?(parameterObject?!=?null)?{??
        ????????????Class?parameterObjectClass?=?parameterObject.getClass();??
        ????????????//校驗該實例的類是否被@SensitiveData所注解??
        ????????????SensitiveData?sensitiveData?=?AnnotationUtils.findAnnotation(parameterObjectClass,?SensitiveData.class);??
        ????????????if?(Objects.nonNull(sensitiveData))?{??
        ????????????????//取出當前當前類所有字段,傳入加密方法??
        ????????????????Field[]?declaredFields?=?parameterObjectClass.getDeclaredFields();??
        ????????????????encryptUtil.encrypt(declaredFields,?parameterObject);??
        ????????????}??
        ????????}??
        ????????return?invocation.proceed();??
        ????}??
        ???
        ????/**??
        ?????*?切記配置,否則當前攔截器不會加入攔截器鏈??
        ?????*/
        ??
        ????@Override??
        ????public?Object?plugin(Object?o)?{??
        ????????return?Plugin.wrap(o,?this);??
        ????}??
        ???
        ????//自定義配置寫入,沒有自定義配置的可以直接置空此方法??
        ????@Override??
        ????public?void?setProperties(Properties?properties)?{??
        ????}??
        }??
        至此完成自定義加密攔截加密。

        2.5 定義解密接口及其實現(xiàn)類

        解密接口,其中result為mapper.xml中resultType的實例。
        public?interface?DecryptUtil?{??
        ???
        ????/**??
        ?????*?解密??
        ?????*??
        ?????*?@param?result?resultType的實例??
        ?????*?@return?T??
        ?????*?@throws?IllegalAccessException?字段不可訪問異常??
        ?????*/
        ??
        ??????T?decrypt(T?result)?throws?IllegalAccessException;??
        ??????
        }??
        解密接口AES工具解密實現(xiàn)類
        public?class?AESDecrypt?implements?DecryptUtil?{??
        ??????
        ????@Autowired??
        ????AESUtil?aesUtil;??
        ??????
        ????/**??
        ?????*?解密??
        ?????*??
        ?????*?@param?result?resultType的實例??
        ?????*?@return?T??
        ?????*?@throws?IllegalAccessException?字段不可訪問異常??
        ?????*/
        ??
        ????@Override??
        ????public??T?decrypt(T?result)?throws?IllegalAccessException?{??
        ????????//取出resultType的類??
        ????????Class?resultClass?=?result.getClass();??
        ????????Field[]?declaredFields?=?resultClass.getDeclaredFields();??
        ????????for?(Field?field?:?declaredFields)?{??
        ????????????//取出所有被EncryptDecryptField注解的字段??
        ????????????SensitiveField?sensitiveField?=?field.getAnnotation(SensitiveField.class);??
        ????????????if?(!Objects.isNull(sensitiveField))?{??
        ????????????????field.setAccessible(true);??
        ????????????????Object?object?=?field.get(result);??
        ????????????????//只支持String的解密??
        ????????????????if?(object?instanceof?String)?{??
        ????????????????????String?value?=?(String)?object;??
        ????????????????????//對注解的字段進行逐一解密??
        ????????????????????field.set(result,?aesUtil.decrypt(value));??
        ????????????????}??
        ????????????}??
        ????????}??
        ????????return?result;??
        ????}??
        }??

        2.6 定義出參解密攔截器

        @Slf4j??
        @Component??
        @Intercepts({??
        ????????@Signature(type?=?ResultSetHandler.class,?method?=?"handleResultSets",?args?=?{Statement.class})??
        })??
        public?class?DecryptInterceptor?implements?Interceptor?
        {??
        ???
        ????@Autowired??
        ????DecryptUtil?aesDecrypt;??
        ???
        ????@Override??
        ????public?Object?intercept(Invocation?invocation)?throws?Throwable?{??
        ????????//取出查詢的結(jié)果??
        ????????Object?resultObject?=?invocation.proceed();??
        ????????if?(Objects.isNull(resultObject))?{??
        ????????????return?null;??
        ????????}??
        ????????//基于selectList??
        ????????if?(resultObject?instanceof?ArrayList)?{??
        ????????????ArrayList?resultList?=?(ArrayList)?resultObject;??
        ????????????if?(!CollectionUtils.isEmpty(resultList)?&&?needToDecrypt(resultList.get(0)))?{??
        ????????????????for?(Object?result?:?resultList)?{??
        ????????????????????//逐一解密??
        ????????????????????aesDecrypt.decrypt(result);??
        ????????????????}??
        ????????????}??
        ????????//基于selectOne??
        ????????}?else?{??
        ????????????if?(needToDecrypt(resultObject))?{??
        ????????????????aesDecrypt.decrypt(resultObject);??
        ????????????}??
        ????????}??
        ????????return?resultObject;??
        ????}??
        ???
        ????private?boolean?needToDecrypt(Object?object)?{??
        ????????Class?objectClass?=?object.getClass();??
        ????????SensitiveData?sensitiveData?=?AnnotationUtils.findAnnotation(objectClass,?SensitiveData.class);??
        ????????return?Objects.nonNull(sensitiveData);??
        ????}??
        ???
        ???
        ????@Override??
        ????public?Object?plugin(Object?target)?{??
        ????????return?Plugin.wrap(target,?this);??
        ????}??
        ???
        ????@Override??
        ????public?void?setProperties(Properties?properties)?{??
        ???
        ????}??
        }??
        至此完成解密攔截器的配置工作。

        3、注解實體類中需要加解密的字段

        圖片
        此時在mapper中,指定paramType=User?resultType=User?便可實現(xiàn)脫離業(yè)務層,基于mybatis攔截器的加解密操作。

        推薦閱讀:

        新來的同事問我 where 1=1 是什么意思

        寫 Java 代碼的14個好習慣

        互聯(lián)網(wǎng)初中高級大廠面試題(9個G)

        內(nèi)容包含Java基礎、JavaWeb、MySQL性能優(yōu)化、JVM、鎖、百萬并發(fā)、消息隊列、高性能緩存、反射、Spring全家桶原理、微服務、Zookeeper、數(shù)據(jù)結(jié)構(gòu)、限流熔斷降級......等技術(shù)棧!

        ?戳閱讀原文領(lǐng)??!? ? ? ? ? ? ? ??? ??? ? ? ? ? ? ? ? ? ?朕已閱?

        瀏覽 110
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            爽 好紧 别夹 喷水欧美 | 金荷娜裸乳无删减版 | 女人脱精光让人桶爽了x66 | 毛片日逼 | 欧美啪啪啪网站 | 91蝌蚪人妻| 成人久久精品 | 美女一级A级视频 | 午夜成人中文字幕 | 欧美成人亚洲精品 |