最近一次迭代,參與了公司數據應用平臺的開發,其中負責的一塊功能早早的就完成了代碼的編寫工作,即將進入測試階段,因為有時間思考和總結代碼編寫中遇到的難題,便想著將代碼做一次重構:其中優化的一個功能就是關於數據平臺敏感欄位的收集 功能描述:數據平臺敏感欄位的收集: 開始的版本: 可以看出邏輯都散落在fo ...
最近一次迭代,參與了公司數據應用平臺的開發,其中負責的一塊功能早早的就完成了代碼的編寫工作,即將進入測試階段,因為有時間思考和總結代碼編寫中遇到的難題,便想著將代碼做一次重構:其中優化的一個功能就是關於數據平臺敏感欄位的收集
功能描述:數據平臺敏感欄位的收集:
提供 service 方法,查詢是否需要掃描表做觸髮式收集,指定具體實例與庫的表,隨機取 N 行(1~max(id) 之間);
a.對每一行的每一個欄位的值(取非 null 非空的值)做正則匹配
b. 對每一行的每一個欄位的值看是否包含了敏感欄位的 key
c. 對每一行的每一個欄位名做匹配;如果匹配,再判斷該欄位為敏感欄位還是疑似敏感欄位,添加到 secret_column 中
開始的版本:
/** * 敏感欄位的收集 * * @param instance * @param schema */ public void collectSecretColumn(String instance, String schema, String table) { //查詢該表是否掃描過 CollectedTable collectedTable = collectedTableService.getCollectedTable(instance, schema, table); if (collectedTable != null) { return; } //隨機獲取n行記錄 JdbcResult query = getPartQueryResult(instance, schema, table); if (query == null || (query != null && StringUtils.isNotBlank(query.getQueryErrorMsg()))) { throw new CjjServerException(500, "系統繁忙,請稍後再試"); } //key為column value為值的集合 Map<String, List<String>> groupMap = convertListToMap(query.getResult()); Set<Map.Entry<String, List<String>>> entries = groupMap.entrySet(); List<SecretContainedKey> secretContainedKeys = secretContainedKeyMapper.listSecretContainedKeys(); List<SecretValueRegex> secretValueRegexs = secretValueRegexService.getSecretValueRegexes(); for (Map.Entry<String, List<String>> entry : entries) { //獲取column String column = entry.getKey(); List<String> values = entry.getValue(); //判斷該欄位是否已經存在在敏感欄位表中 boolean secretColumnExist = isSecretColumnExist(instance, schema, table, column); if (secretColumnExist) { continue; } //c:對欄位名做匹配 boolean isValueContaninsKey = secretContainedKeyService.columnKeyIsContainsKey(instance, schema, table, secretContainedKeys, column); if (isValueContaninsKey) { continue; } //b:欄位的值是否包含敏感欄位的key boolean isContainsKey = secretContainedKeyService.columnValueIsContainsKey(instance, schema, table, secretContainedKeys, column, values); if (isContainsKey) { continue; } //a:通過正則匹配欄位值 secretValueRegexService.regexMatch(instance, schema, table, column, values, secretValueRegexs); } CollectedTable collected = CollectedTable .builder() .instanceName(instance) .schemaName(schema) .tableName(table) .build(); collectedTableMapper.save(collected); }
可以看出邏輯都散落在for迴圈中
通過責任鏈模式:後代碼:
/** * 敏感欄位的收集 * * @param instance * @param schema */ public void collectSecretColumn(String instance, String schema, String table) { //查詢該表是否掃描過 CollectedTable collectedTable = collectedTableService.getCollectedTable(instance, schema, table); if (collectedTable != null) { return; } //隨機獲取n行記錄 JdbcResult query = getPartQueryResult(instance, schema, table); if (query == null || (query != null && StringUtils.isNotBlank(query.getQueryErrorMsg()))) { throw new CjjServerException(500, "系統繁忙,請稍後再試"); } //key為column value為值的集合 Map<String, List<String>> groupMap = convertListToMap(query.getResult()); Set<Map.Entry<String, List<String>>> entries = groupMap.entrySet(); secretValueRegexHandler.setSuccessor(secretValueContainedKeyHandler); secretValueContainedKeyHandler.setSuccessor(secretColumnContainedKeyHandler); for (Map.Entry<String, List<String>> entry : entries) { //獲取column String column = entry.getKey(); List<String> values = entry.getValue(); //判斷該欄位是否已經存在在敏感欄位表中 boolean secretColumnExist = isSecretColumnExist(instance, schema, table, column); if (secretColumnExist) { continue; } secretValueRegexHandler.handleCollect(instance, schema, table, column, values); } CollectedTable collected = CollectedTable .builder() .instanceName(instance) .schemaName(schema) .tableName(table) .build(); collectedTableMapper.save(collected); }
可以看出這邊的代碼量減少了,看起來結構更清晰了
為了方便理解:我會列出部分代碼供大家參考
package cn.caijiajia.firekylin.service.secret; import java.util.List; /** * 責任鏈設計模式 * * @author chenlang * date 2018/7/13 */ public abstract class CollectSecretColumnHandler { protected CollectSecretColumnHandler successor; public abstract void handleCollect(String instance, String schema, String table, String column, List<String> values); /** * 獲取責任對象 */ public CollectSecretColumnHandler getSuccessor() { return successor; } /** * 設置後繼的責任對象 */ public void setSuccessor(CollectSecretColumnHandler successor) { this.successor = successor; } }
package cn.caijiajia.firekylin.service.secret; import cn.caijiajia.firekylin.domain.SecretContainedKey; import cn.caijiajia.firekylin.mapper.SecretContainedKeyMapper; import cn.caijiajia.firekylin.service.SecretColumnService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * @author chenlang * date 2018/7/13 */ @Component public class SecretColumnContainedKeyHandler extends CollectSecretColumnHandler { @Autowired private SecretColumnService secretColumnService; @Autowired private SecretContainedKeyMapper secretContainedKeyMapper; @Override public void handleCollect(String instance, String schema, String table, String column, List<String> values) { List<SecretContainedKey> secretContainedKeys = secretContainedKeyMapper.listSecretContainedKeys(); boolean columnKeyIsContainsKey = columnKeyIsContainsKey(instance, schema, table, secretContainedKeys, column); if (!columnKeyIsContainsKey) { } } public boolean columnKeyIsContainsKey(String instance, String schema, String table, List<SecretContainedKey> secretContainedKeys, String column) { SecretContainedKey secretContainedKeyByColumn = getSecretContainedKeyByColumn(column, secretContainedKeys); if (secretContainedKeyByColumn != null) { secretColumnService.saveSecretColumn(instance, schema, table, column, secretContainedKeyByColumn.getSecretType(), secretContainedKeyByColumn.getColumnType()); return true; } return false; } /** * 欄位名是否包含敏感的key * * @param column * @param secretContainedKeys * @return */ public SecretContainedKey getSecretContainedKeyByColumn(String column, List<SecretContainedKey> secretContainedKeys) { Map<String, SecretContainedKey> keysMap = secretContainedKeys.stream().collect(Collectors.toMap(SecretContainedKey::getContainedKey, a -> a)); Set<Map.Entry<String, SecretContainedKey>> entries = keysMap.entrySet(); for (Map.Entry<String, SecretContainedKey> entry : entries) { String key = entry.getKey(); boolean contains = column.toLowerCase().contains(key); if (contains) { return keysMap.get(key); } } return null; } }
package cn.caijiajia.firekylin.service.secret; import cn.caijiajia.firekylin.domain.SecretContainedKey; import cn.caijiajia.firekylin.mapper.SecretContainedKeyMapper; import cn.caijiajia.firekylin.service.SecretColumnService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; /** * @author chenlang * date 2018/7/13 */ @Component public class SecretValueContainedKeyHandler extends CollectSecretColumnHandler { @Autowired private SecretColumnService secretColumnService; @Autowired private SecretContainedKeyMapper secretContainedKeyMapper; @Override public void handleCollect(String instance, String schema, String table, String column, List<String> values) { List<SecretContainedKey> secretContainedKeys = secretContainedKeyMapper.listSecretContainedKeys(); boolean columnValueIsContainsKey = columnValueIsContainsKey(instance, schema, table, secretContainedKeys, column, values); if (!columnValueIsContainsKey) { getSuccessor().handleCollect(instance, schema, table, column, values); } } public boolean columnValueIsContainsKey(String instance, String schema, String table, List<SecretContainedKey> secretContainedKeys, String column, List<String> values) { for (SecretContainedKey secretContainedKey : secretContainedKeys) { boolean isSecretColumnContainsKey = isSecretColumnContainsKey(values, secretContainedKey); if (isSecretColumnContainsKey) { secretColumnService.saveSecretColumn(instance, schema, table, column, secretContainedKey.getSecretType(), secretContainedKey.getColumnType()); return true; } } return false; } /** * 欄位值是否包含敏感欄位的key * * @param columnValues * @param secretContainedKey * @return */ public boolean isSecretColumnContainsKey(List<String> columnValues, SecretContainedKey secretContainedKey) { for (String columnValue : columnValues) { if (columnValue.toLowerCase().contains(secretContainedKey.getContainedKey())) { return true; } } return false; } }
package cn.caijiajia.firekylin.service.secret; import cn.caijiajia.firekylin.constant.SecretType; import cn.caijiajia.firekylin.domain.SecretValueRegex; import cn.caijiajia.firekylin.service.SecretColumnService; import cn.caijiajia.firekylin.service.SecretValueRegexService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.util.List; import java.util.regex.Pattern; /** * 正則匹配收集敏感欄位 * * @author chenlang * date 2018/7/13 */ @Component public class SecretValueRegexHandler extends CollectSecretColumnHandler { @Autowired private SecretColumnService secretColumnService; @Autowired private SecretValueRegexService secretValueRegexService; @Override public void handleCollect(String instance, String schema, String table, String column, List<String> values) { List<SecretValueRegex> secretValueRegexs = secretValueRegexService.getSecretValueRegexes(); boolean regexMatch = regexMatch(instance, schema, table, column, values, secretValueRegexs); if (!regexMatch) { if (getSuccessor() != null) { getSuccessor().handleCollect(instance, schema, table, column, values); } } } public boolean regexMatch(String instance, String schema, String table, String column, List<String> values, List<SecretValueRegex> secretValueRegexs) { for (SecretValueRegex secretValueRegex : secretValueRegexs) { boolean secretByRegex = isSecretByRegex(values, secretValueRegex.getPattern()); if (secretByRegex) { secretColumnService.saveSecretColumn(instance, schema, table, column, SecretType.SECRECT, secretValueRegex.getCode()); return true; } } return false; } /** * 欄位值是否匹配正則表達式 * * @param columnValues * @return */ public boolean isSecretByRegex(List<String> columnValues, Pattern compile) { if (CollectionUtils.isEmpty(columnValues)) { return false; } for (String columnValue : columnValues) { boolean isSecret = compile.matcher(columnValue).matches(); if (!isSecret) { return false; } } return true; } }
現在每種情況對應一種handler,同時繼承自
CollectSecretColumnHandler