欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

背景

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

方案

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

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

手動(dòng)加解密

這是首先被提出來(lái)的方案。該方案做法是

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

優(yōu)點(diǎn):

  • 暫無(wú);

缺點(diǎn):

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

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

自動(dòng)加解密

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

優(yōu)點(diǎn):

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

缺點(diǎn):

  • 暫無(wú);

實(shí)現(xiàn)

編碼

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

加解密策略

public interface SensitiveStrategy {

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

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

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

自定義注解

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

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

    Class<? extends SensitiveStrategy> sensitiveStrategy();
}

MyBatis攔截器

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

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

我們知道,MyBatis的攔截器插件,可以對(duì)四大組件Executor、StatementHandler、ParameterHandler、ResultSetHandler進(jìn)行攔截,由于我們只需要對(duì)入?yún)⒑徒Y(jié)果進(jìn)行攔截和修改,所以只需指定攔截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ù)對(duì)象
            Object parameterObj = ReflectUtil.getFieldValue(parameterHandler, "parameterObject");
            if (parameterObj != null){
                //獲取參數(shù)對(duì)象內(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的,所以我們將所有實(shí)現(xiàn)的加解密策略對(duì)象,交給Spring容器管理。在MyBatis攔截器中,直接從Spring容器中獲取對(duì)應(yīng)加解密策略使用接口。

上線

上線階段,我們分為以下幾個(gè)步驟實(shí)施

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

其他問(wèn)題

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

敏感字段like搜索

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

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

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

敏感字段group by

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

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

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

相關(guān)文章

最新評(píng)論