亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

MyBatis實現(xiàn)字段加解密的實踐

 更新時間:2023年11月14日 10:40:27   作者:十年培訓(xùn)經(jīng)驗的菜包  
為了數(shù)據(jù)安全問題,有時候需要將部分敏感字段加密后再入庫,本文主要介紹了MyBatis實現(xiàn)字段加解密的實踐,具有一定的參考價值,感興趣的可以了解一下

背景

互聯(lián)網(wǎng)系統(tǒng)充斥著各種敏感信息,包括各種個人信息、商業(yè)信息等等。按照要求,不允許隱私信息明文存儲,需要進行加密處理,防止造成隱私泄露的風(fēng)險。
我司作為一個跨境電商公司,各系統(tǒng)中,自然免不了涉及各類敏感信息,并且對各類敏感信息的安全級別進行了劃分,不同等級的加密要求級別有一定的差別。

方案

由于不同的敏感數(shù)據(jù)需要使用不同的加解密策略,在MySQL層面,無法滿足需求,所以只能在應(yīng)用代碼層面進行實現(xiàn)。但需要考慮幾個點

  • 盡可能減少對業(yè)務(wù)代碼侵入性;
  • 以最小的風(fēng)險進行改動;
  • 方案可復(fù)用,方便拓展;

手動加解密

這是首先被提出來的方案。該方案做法是

  • 在任何涉及到讀取敏感字段的業(yè)務(wù)代碼中,進行手動解密操作。
  • 在任何涉及到寫入敏感字段的業(yè)務(wù)代碼中,進行手動加密操作。

優(yōu)點:

  • 暫無;

缺點:

  • 侵入業(yè)務(wù)代碼,業(yè)務(wù)開發(fā)甚至需要關(guān)注不同級別的加解密策略;
  • 老舊系統(tǒng)調(diào)用復(fù)雜,可能出現(xiàn)重復(fù)加密或重復(fù)解密,導(dǎo)致無法復(fù)原原始數(shù)據(jù),風(fēng)險數(shù)據(jù)非常高;
  • 若數(shù)據(jù)安全級別變動,加密策略升級,所有可能涉及到的業(yè)務(wù)代碼都需要變更,風(fēng)險半徑無法預(yù)測;

綜上所述,該方案完全無法滿足我們對方案的要求,屬于最笨的方案,可以直接say no。

自動加解密

由于無法在MySQL層面實現(xiàn),又希望盡可能減少對業(yè)務(wù)代碼的侵入性,那么任務(wù)只能落在ORM框架或半ORM框架上。我們系統(tǒng)使用的是MyBatis,那么利用MyBatis的插件機制來實現(xiàn)自動加解密,是個不錯的選擇。

優(yōu)點:

  • 業(yè)務(wù)代碼無需改造,幾乎對業(yè)務(wù)代碼無入侵性;
  • 加解密統(tǒng)一入口,風(fēng)險可控;
  • 方案多個系統(tǒng)通用,加解密策略可隨時拓展;

缺點:

  • 暫無;

實現(xiàn)

編碼

由于敏感數(shù)據(jù)被劃分成多個不同級別,各個級別使用的加解密算法不同,所以面對這種不同加密算法的場景,策略模式非常適合;

加解密策略

public interface SensitiveStrategy {

    /**
     * 加密
     */
    String encrypt(String value);

    /**
     * 解密
     */
    String decrypt(String value);
}

各種加解密算法,只要實現(xiàn)該接口即可,此處略。

自定義注解

自定義一個字段上的注解,目的是為了讓MyBatis攔截器識別哪些字段需要加解密,加解密的策略是什么。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface SensitiveField {

    Class<? extends SensitiveStrategy> sensitiveStrategy();
}

MyBatis攔截器

我們需要定義一個MyBatis攔截器,該攔截器的作用有以下:

  • 攔截入?yún)?,識別被@SensitiveField注解的字段,并使用指定加密策略,對字段內(nèi)容進行加密;
  • 攔截查詢結(jié)果,識別被@SensitiveField注解的字段,并使用指定的解密策略,對字段內(nèi)容進行解密;

我們知道,MyBatis的攔截器插件,可以對四大組件Executor、StatementHandler、ParameterHandler、ResultSetHandler進行攔截,由于我們只需要對入?yún)⒑徒Y(jié)果進行攔截和修改,所以只需指定攔截ParameterHandler、ResultSetHandler即可。

@Slf4j
@Component
@Intercepts(value = {
        @Signature(type = ParameterHandler.class,method = "setParameters",args = {PreparedStatement.class}),
        @Signature(type = ResultSetHandler.class,method = "handleResultSets",args = {Statement.class})
})
public class SensitiveInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        Object target = invocation.getTarget();
        //入?yún)⒓用?
        if (target instanceof ParameterHandler parameterHandler){
            //獲取參數(shù)對象
            Object parameterObj = ReflectUtil.getFieldValue(parameterHandler, "parameterObject");
            if (parameterObj != null){
                //獲取參數(shù)對象內(nèi)的字段
                Arrays.stream(ReflectUtil.getFields(parameterObj.getClass()))
                        .filter(field -> String.class.equals(field.getType()))
                        .filter(field -> field.getAnnotation(SensitiveField.class)!=null )
                        .filter(field -> ReflectUtil.getFieldValue(parameterObj,field) != null)
                        .forEach(field -> {
                            SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
                            Class<? extends SensitiveStrategy> strategyClazz = sensitiveField.sensitiveStrategy();
                            if (strategyClazz != null){
                                SensitiveStrategy strategy = SpringContext.getBean(strategyClazz);
                                String encrypt = strategy.encrypt(ReflectUtil.getFieldValue(parameterObj, field).toString());
                                ReflectUtil.setFieldValue(parameterObj,field,encrypt);
                            }
                        });
                ReflectUtil.setFieldValue(parameterHandler,"parameterObject",parameterObj);
            }
        }

        Object resultObj = invocation.proceed();

        //出參解密
        if (resultObj != null && target instanceof ResultSetHandler){
            List<?> resultList = (List<?>) resultObj;
            for (Object result : resultList) {
                if (!SimpleTypeRegistry.isSimpleType(result.getClass())){
                    Arrays.stream(ReflectUtil.getFields(result.getClass()))
                            .filter(field -> String.class.equals(field.getType()))
                            .filter(field -> field.getAnnotation(SensitiveField.class)!=null )
                            .filter(field -> ReflectUtil.getFieldValue(result,field) != null)
                            .forEach(field -> {
                                SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
                                Class<? extends SensitiveStrategy> strategyClazz = sensitiveField.sensitiveStrategy();
                                if (strategyClazz != null){
                                    SensitiveStrategy strategy = SpringContext.getBean(strategyClazz);
                                    String decrypt = strategy.decrypt(ReflectUtil.getFieldValue(result, field).toString());
                                    ReflectUtil.setFieldValue(result,field,decrypt);
                                }
                            });
                }
            }
            resultObj = resultList;
        }
        return resultObj;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target,this);
    }
}

由于系統(tǒng)是基于SpringBoot的,所以我們將所有實現(xiàn)的加解密策略對象,交給Spring容器管理。在MyBatis攔截器中,直接從Spring容器中獲取對應(yīng)加解密策略使用接口。

上線

上線階段,我們分為以下幾個步驟實施

  • 臨時關(guān)閉敏感數(shù)據(jù)列的修改功能入口,避免出現(xiàn)備份后源數(shù)據(jù)變更;
  • 備份敏感字段列數(shù)據(jù),小表DBA直接備份,大表編寫代碼分批備份;
  • 利用apollo控制讀取時不解密,寫入時加密,對對應(yīng)字段進行一次讀取后更新即可完成加密;
  • 觀察涉及的業(yè)務(wù)功能讀取是否正常;
  • 開啟敏感數(shù)據(jù)修改功能入口,觀察系統(tǒng)是否正常;

其他問題

其實在整個過程中,實現(xiàn)功能相關(guān)的編碼并不復(fù)雜。除此之外,需要去識別并解決其他的一些問題,這期間花費了更多的時間,例如

敏感字段like搜索

字段加密后存儲,就無法直接使用like關(guān)鍵字搜索。其實經(jīng)過我司安全部門評估,敏感字段也不允許進行模糊搜索,有數(shù)據(jù)泄露風(fēng)險,所以在產(chǎn)品方案層面,直接砍掉了類似功能;
若是一定要保留改功能,也可以使用以下方案

  • 拓展一個新字段,用于模糊搜索,類型為text;
  • 對原始字符進行分割,可按每N個字符為一項進行分割,分割后每一項使用固定加密算法加密,再使用固定字符對每一項進行拼接,形成新的字符串,保存到新字段;
  • like查詢時,同樣也是分割、加密、拼接的方式;

注意,like字段需要使用text類型,性能會很低。

敏感字段group by

對于一些敏感級別較低的字段,采用了固定加密方式(即多次對相同的數(shù)據(jù)進行加密后結(jié)果不變),此時由于結(jié)果不變,可以直接使用加密后的字段進行g(shù)roup by;

但是對于敏感級別較高的字段,我司采用了動態(tài)加密方式(即多次對相同的數(shù)據(jù)加密結(jié)果不一致),此時由于結(jié)果不一致,無法進行g(shù)roup by操作。解決方案的拓展多一列,對源數(shù)據(jù)使用固定加密后,將結(jié)果存進該拓展字段,group by業(yè)務(wù)使用。

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

相關(guān)文章

最新評論