成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

企業級數據脫敏方案!

數據庫 其他數據庫
本文總結了企業中脫敏方案實現,包含數據庫脫敏、日志脫敏、輸出脫敏,并貼上關鍵實現代碼。能夠滿足業務的要求。

最近幾年經常發生用戶數據泄漏的事件,給企業帶來危機。隨著用戶對個人隱私數據的重視和法律法規的完善,數據安全顯得愈發重要。一方面可以加強權限管理,減少能夠接觸數據的人員以及導出數據加強審批。另一方面,還需要從技術上對用戶隱私數據進行脫敏處理,提高數據的安全性。

數據脫敏方法有很多種,大致可以按照以下進行分類:

  1. 隱藏法: 只顯示敏感信息的部分內容,其他部分進行遮擋,比較常見使用星號替代。這種方式日常比較多見,比如手機號,銀行卡號等只顯示后面和后面幾位,好處是雖然只是部分內容顯示,但足夠提供有效信息,同時不會暴露完整數據。
  2. 混淆法: 對原有數據截斷、替換、隱藏、數字進行隨機移位,使得原有數據完全失真或者部分失真,混淆真假。
  3. 加密: 通過加密密鑰和算法對敏感數據進行加密得到密文,密文可見但是完全沒有可讀意義,是脫敏最徹底的方法。其中對稱加密還能密鑰解密可以從密文恢復原始數據。比如密碼保存采用非對稱加密,手機號存儲時采用對稱加密。

用戶的敏感數據包含姓名、電話號碼、身份證、銀行卡號、電子郵件、家庭住址、登錄密碼等等。需要考慮數據的敏感程度、數據安全要求以及實際業務使用場景選擇合適的脫敏方法。Hutool包里面提供了許多常用的脫敏方法。

企業脫敏方案

企業如何實現脫敏?我們先來看典型的系統數據交互鏈路,數據需要經過數據庫、后端應用、app端。

圖片圖片

  • 數據庫側: 數據庫保存了原始數據,有權限人員可以查看數據和導出數據。
  • 后端應用內: 后端應用中會打印相關日志,數據通過日志得到了存儲下來。通過日志,能夠得到原始數據。
  • 應用輸出: app側能夠從后端讀取到原始數據。

數據庫脫敏方案

數據庫脫敏方法根據業務具體要求選擇合適脫敏方法。脫敏地點可以在應用中手動脫敏,當然這種方法不常用,改動點多對業務侵入大。

另外一種方案在ORM框架中修改sql實現,其中mybatis框架為java后端系統中最常用的框架。mybatis自帶攔截器擴展,允許在映射語句執行過程中的某一點進行攔截調用。關注公眾號:碼猿技術專欄,回復關鍵詞:1111 獲取阿里內部Java性能調優手冊!默認情況下,MyBatis 允許使用插件來攔截的方法調用包括:

  • Executor: 攔截執行器的方法,例如 update、query、commit、rollback 等??梢杂脕韺崿F緩存、事務、分頁等功能。
  • ParameterHandler: 攔截參數處理器的方法,例如 setParameters 等。可以用來轉換或加密參數等功能。
  • ResultSetHandler: 攔截結果集處理器的方法,例如 handleResultSets、handleOutputParameters 等。可以用來轉換或過濾結果集等功能。
  • StatementHandler: 攔截語句處理器的方法,例如 prepare、parameterize、batch、update、query 等。可以用來修改 SQL 語句、添加參數、記錄日志等功能。

Mybatis執行流程Mybatis執行流程

數據庫脫敏另外一個問題是歷史數據問題。歷史原因最開始的技術方案保存明文,所以脫敏時需要做到平滑脫敏。要做到平滑脫敏,可按照如下流程:

  • 新增脫敏字段: 在源表上新增脫敏字段。
  • 數據雙寫: 源字段和脫敏字段都寫入數據。
  • 歷史數據遷移: 歷史數據遷移,刷入脫敏字段。
  • 讀切換脫敏字段: 從脫敏字段讀取數據返回。
  • 清空源字段: 確保所有流程都正確的情況下,清空源字段。

本文mybatis實現數據庫加解密為例。

1.表里面新增脫敏字段,示例中脫敏新字段格式規范為源字段添加encrypt后綴。vo里面添加脫敏注解標記。

public class Employee {

    private Long id;
    private String name;

    @EncryptTag
    private String mobile;
    private String mobileEncrypt;
    private String email;
    private double salary;
}

2.實現自定義攔截。

/***
** 加密攔截
***/
@Intercepts({@Signature(
        type = Executor.class,
        method = "update",
        args = {MappedStatement.class, Object.class}
), @Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
), @Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)})
public class EncryptPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];
        Object param = invocation.getArgs()[1];
        PluginService.encrypt(invocation, param);
        return invocation.proceed();
    }


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

    @Override
    public void setProperties(Properties properties) {

    }
}


/***
** 解密攔截
***/
@Intercepts({@Signature(
        type = ResultSetHandler.class,
        method = "handleResultSets",
        args = {Statement.class}
)})
public class DecryptPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
            Object result = invocation.proceed();
            if (result != null && result instanceof List) {
                this.decrypt(((List) result).iterator());
            }
            return result;
    }


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

    @Override
    public void setProperties(Properties properties) {

    }

    private void decrypt(Iterator iterator) throws Throwable {
        while(iterator.hasNext()) {
            Object object = iterator.next();
            PluginService.decrypt(object);
        }
    }
}

3.實現sql修改,完成加解密邏輯。

public class PluginService {

    privatestaticfinal Logger LOGGER = LoggerFactory.getLogger(PluginService.class);

    privatestaticfinal Map<String, List<Field>> ENCRYPT_TAG_FIELDS = new ConcurrentHashMap();

    public static void encrypt(Invocation invocation, Object object) throws Throwable {
        if (object.getClass().isArray()) {
            int length = Array.getLength(object);
            if (length <= 0) {
                return;
            }

            for (int i = 0; i < length; ++i) {
                encryptSingleObject(Array.get(object, i));
            }
        } elseif (object instanceof Collection) {
            Collection collection = (Collection) object;
            Iterator itr = collection.iterator();
            while (itr.hasNext()) {
                Object item = itr.next();
                encryptSingleObject(item);
            }
        } else {
            encryptSingleObject(object);
        }
    }


    private static void encryptSingleObject(Object object) throws Throwable {
        if (object != null) {
            String className = object.getClass().getName();
            List<Field> EncryptTagFields = ENCRYPT_TAG_FIELDS.get(className);
            if (EncryptTagFields == null) {
                EncryptTagFields = findEncryptTagFields(object);
                ENCRYPT_TAG_FIELDS.putIfAbsent(className, EncryptTagFields);
            }
            encryptFields(object, EncryptTagFields);
        }
    }

    private static void encryptFields(Object object, List<Field> EncryptTagFields) throws Throwable {
        if (object != null && !EncryptTagFields.isEmpty()) {
            String[] originalValues = new String[EncryptTagFields.size()];

            for(int i = 0; i < EncryptTagFields.size(); ++i) {
                Field field = (Field)EncryptTagFields.get(i);
                String value = (String)field.get(object);
                originalValues[i] = value;
            }

            for(int i = 0; i < EncryptTagFields.size(); ++i) {
                Field field = (Field)EncryptTagFields.get(i);
                String value = originalValues[i];

                if (value == null) {
                    continue;
                }
                Field encryptField = getEncryptField(object, field);
                if (encryptField == null) {
                    continue;
                }
                String encryptValue = encryptFieldValue(value);
                encryptField.set(object, encryptValue);
                field.set(object, null);

            }
        }
    }

    private static String encryptFieldValue(String value) {
        String encryptValue = value + "encrypt";
        return encryptValue;
    }

    public static void decrypt(Object object) throws Throwable {
        if (object == null) {
            return;
        }
        String className = object.getClass().getName();
        List<Field> encryptTagFields = ENCRYPT_TAG_FIELDS.get(className);
        if (encryptTagFields == null) {
            encryptTagFields = findEncryptTagFields(object);
            ENCRYPT_TAG_FIELDS.putIfAbsent(className, encryptTagFields);
        }
        decryptFields(object, encryptTagFields);
    }


    private static void decryptFields(Object object, List<Field> encryptTagFields) throws Throwable {
        if (encryptTagFields.isEmpty()) {
            return;
        }

        for (int i = 0; i < encryptTagFields.size(); ++i) {
            Field field = encryptTagFields.get(i);
            Field encryptField = getEncryptField(object, field);
            Object fieldValue = encryptField.get(object);
            if (fieldValue == null) {
                continue;
            }
            if (fieldValue instanceof String) {
                String value = (String) fieldValue;
                value = AesUtil.decrypt(value);
                field.set(object, value);
                encryptField.set(object, null);
            }
        }
    }

    private static List<Field> findEncryptTagFields(Object object) {
        Class clazz = object.getClass();

        List<Field> fieldList = new ArrayList<>();
        for(; clazz != null; clazz = clazz.getSuperclass()) {
            Field[] declaredFields = clazz.getDeclaredFields();
            int length = declaredFields.length;

            for(int index = 0; index < length; ++index) {
                Field field = declaredFields[index];
                if (field.getAnnotation(EncryptTag.class) != null) {
                    if (field.getType() == String.class) {
                        field.setAccessible(true);
                        fieldList.add(field);
                    } else {
                        LOGGER.error("@EncryptTag should be used on String field. class: {}, fieldName: {}", clazz.getName(), field.getName());
                    }
                }
            }
        }

        return fieldList;
    }

    private static Field getEncryptField(Object object, Field field) throws Exception {
        String encryptFieldName = AesUtil.encrypt(field.getName());
        Field encyptField = getField(object, encryptFieldName);
        if (encyptField == null) {
            thrownew Exception(object.getClass() + "對象沒有對應的加密字段:" + encryptFieldName);
        } else {
            encyptField.setAccessible(true);
            return encyptField;
        }
    }

    public static Field getField(Object object, String fieldName) {
        for(Class clazz = object.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
            Field[] var3 = clazz.getDeclaredFields();
            int var4 = var3.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                Field field = var3[var5];
                if (field.getName().equals(fieldName)) {
                    return field;
                }
            }
        }

        returnnull;
    }
}

日志脫敏方案

日志脫敏,核心在于序列化時對于敏感字段修改其序列化方式。各大序列化工具一般都有序列化自定義功能,關注公眾號:碼猿技術專欄,回復關鍵詞:IDEA 獲取最新版IDEA破解腳本!本文以fastjson為例講解實現,實現方式有兩種:

  • 基于注解@JSONField實現
  • 基于序列化過濾器

@JSONField方式不建議使用,對業務入侵太大。另外一種繼續序列化過濾器,fastjson提供了多種SerializeFilter

  • PropertyPreFilter 根據PropertyName判斷是否序列化
  • PropertyFilter 根據PropertyName和PropertyValue來判斷是否序列化
  • NameFilter 修改Key,如果需要修改Key,process返回值則可
  • ValueFilter 修改Value
  • BeforeFilter 序列化時在最前添加內容
  • AfterFilter 序列化時在最后添加內容

通過實現ValueFilter自定義序列化擴展,針對目標類以及字段進行脫敏返回。

核心代碼簡化如下:

public class FastjsonValueFilter implements ValueFilter {
    
    @Override
    public Object process(Object object, String name, Object value) {
        if (needDesensitize(object, name)) {
            return desensitize(value);
        }
    }
}    
String s = JSON.toJSONString(new Person("131xxxx1552","123@163.com"),new FastjsonValueFilter());

在標記脫敏字段以及對應方法時,可以通過配置的方法, 對類相關的脫敏字段以及方法進行封裝。要求不高的話添加響應的注解也可實現。

輸出脫敏

在輸出層織入切面進行攔截,在切面內實現脫敏邏輯。實現邏輯跟日志脫敏類似,需要對脫敏字段進行標記以及對應脫敏方法。

如果是Spring Boot集成,配置 Spring MVC 的話只需繼承 WebMvcConfigurer 覆寫 configureMessageConverters方法,支持全局和指定類脫敏配置,示例如下:

@Configuration
publicclass FastJsonWebSerializationConfiguration implements WebMvcConfigurer {

    @Bean(name = "httpMessageConverters")
    public HttpMessageConverters fastJsonHttpMessageConverters() {
        // 1.定義一個converters轉換消息的對象
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        // 2.添加fastjson的配置信息,比如: 是否需要格式化返回的json數據
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
        // 中文亂碼解決方案
        List<MediaType> mediaTypes = new ArrayList<>();
        //設定json格式且編碼為UTF-8
        mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        fastConverter.setSupportedMediaTypes(mediaTypes);
        
        //添加全局自定義脫敏
        fastJsonConfig.setSerializeFilters(new ValueDesensitizeFilter());
       //添加指定類脫敏方法
        Map<Class<?>, SerializeFilter> classSerializeFilters = new HashMap<>();
        classSerializeFilters.put(Employee.class, new FastjsonValueFilter());
        fastJsonConfig.setClassSerializeFilters(classSerializeFilters);

        
        // 3.在converter中添加配置信息
        fastConverter.setFastJsonConfig(fastJsonConfig);
        // 4.將converter賦值給HttpMessageConverter
        HttpMessageConverter<?> converter = fastConverter;
        // 5.返回HttpMessageConverters對象
        returnnew HttpMessageConverters(converter);
    }
}

總結

本文總結了企業中脫敏方案實現,包含數據庫脫敏、日志脫敏、輸出脫敏,并貼上關鍵實現代碼。能夠滿足業務的要求。


責任編輯:武曉燕 來源: 碼猿技術專欄
相關推薦

2009-03-19 09:49:00

華為數據備份賽門鐵克

2022-01-04 20:34:00

數據安全Relay

2022-01-06 20:00:39

數據企業安全

2022-01-05 20:16:52

Sentry Relay 數據安全

2022-01-08 15:08:17

項目配置Sentry

2022-01-09 21:46:22

安全數據Sentry

2022-01-12 23:54:27

Sentry企業級安全

2022-01-07 18:07:16

數據安全監控

2022-06-08 08:38:21

云存儲數據安全

2021-06-21 11:57:04

數據中臺數字化轉型數字化

2010-12-14 19:56:32

IBM

2022-10-13 09:38:01

數據建設

2012-04-13 13:58:52

數據加密

2010-04-26 15:27:07

互聯網

2009-04-27 17:12:11

數據保護EDPSafeNet

2019-11-06 10:43:20

HBaseSpark數據處理平臺

2013-07-19 10:21:02

數據中心四維模型評價

2011-02-24 10:58:16

數據庫開源

2014-08-18 09:01:09

Teradata數據倉庫

2022-03-09 16:53:38

HiveSpark SQL數據湖
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久精品播放 | 五月香婷婷 | avmans最新导航地址 | 日韩欧美国产精品综合嫩v 一区中文字幕 | 91传媒在线观看 | 欧美一区二区 | 国产十日韩十欧美 | 精品国产一区探花在线观看 | av在线免费观看不卡 | 99精品国产一区二区三区 | 九九导航 | 在线免费看黄 | 国产高清在线精品 | 欧美精品日韩精品 | 国产在线播放av | 91视频观看 | 黄色av网站在线观看 | 99久久国产精 | 亚洲第一av| 日本精品久久久久久久 | 欧美日韩不卡 | 噜啊噜在线| 丝袜美腿一区二区三区动态图 | 精品国产乱码久久久久久88av | 国产精品久久久久久一级毛片 | 日韩精品一区二区三区免费视频 | 人人干人人爽 | 粉嫩av在线 | 色呦呦在线 | 成人国产精品久久 | 欧美一区二区在线视频 | 一级毛片色一级 | 在线中文字幕第一页 | 91欧美激情一区二区三区成人 | 天天操天天摸天天爽 | 三级av免费 | 精品一区电影 | 久久91精品国产一区二区 | 综合一区| 999精品视频 | 欧美精品 在线观看 |