From d23efef219d6acd3bde84ddb0506d8b53fb087b1 Mon Sep 17 00:00:00 2001 From: limbo Date: Thu, 15 Sep 2022 15:59:57 +0800 Subject: [PATCH 1/9] Sensitive information encryption. --- .../config/properties/EncryptProperties.java | 59 ++++++++++++++ .../monitor/handler/CryptoTypeHandler.java | 58 ++++++++++++++ .../frostmourne/monitor/tool/AESUtils.java | 80 +++++++++++++++++++ .../src/main/resources/application.properties | 7 ++ 4 files changed, 204 insertions(+) create mode 100644 frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/config/properties/EncryptProperties.java create mode 100644 frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/handler/CryptoTypeHandler.java create mode 100644 frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/tool/AESUtils.java diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/config/properties/EncryptProperties.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/config/properties/EncryptProperties.java new file mode 100644 index 00000000..0f2813d8 --- /dev/null +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/config/properties/EncryptProperties.java @@ -0,0 +1,59 @@ +package com.autohome.frostmourne.monitor.config.properties; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +/** + * 加密配置类 + * + * @author limbo + * @since 2022/9/14 13:48 + */ +@Configuration +public class EncryptProperties implements InitializingBean { + + private static EncryptProperties prop = null; + + public static EncryptProperties getInstance () { + return prop; + } + + @Override + public void afterPropertiesSet() throws Exception { + prop = this; + } + + /** + * 密钥 + */ + @Value("${encrypt.key:EX31$@*^ac1}") + private String key; + + /** + * 敏感字段列表 + * + * 默认会对 data_source表 properties字段值进行加密 + * 对于username, password等配置等敏感字段也要进行加解密操作,避免在前端泄漏 + */ + @Value("#{'${encrypt.sensitives:username,password}'.split(',')}") + private List sensitiveFields; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public List getSensitiveFields() { + return sensitiveFields; + } + + public void setSensitiveFields(List sensitiveFields) { + this.sensitiveFields = sensitiveFields; + } +} diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/handler/CryptoTypeHandler.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/handler/CryptoTypeHandler.java new file mode 100644 index 00000000..1a7dd26c --- /dev/null +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/handler/CryptoTypeHandler.java @@ -0,0 +1,58 @@ +package com.autohome.frostmourne.monitor.handler; + +import com.autohome.frostmourne.monitor.tool.AESUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedTypes; +import org.apache.ibatis.type.TypeHandler; + +import java.sql.*; +import java.util.Objects; + +/** + * 加解密mybatis TypeHandler + * + * @author limbo + * @since 2022/9/9 15:08 + */ +@MappedTypes({String.class}) +public class CryptoTypeHandler implements TypeHandler { + + /** + * 设置加密 + */ + @Override + public void setParameter(PreparedStatement preparedStatement, int i, String parameter, JdbcType jdbcType) throws SQLException { + if (StringUtils.isNotBlank(parameter)) { + preparedStatement.setString(i, AESUtils.encrypt(parameter)); + } else { + preparedStatement.setNull(i, Types.VARCHAR); + } + } + + /** + * 设置解密 + */ + @Override + public String getResult(ResultSet resultSet, String columnName) throws SQLException { + return decrypt(resultSet.getString(columnName)); + } + + @Override + public String getResult(ResultSet resultSet, int columnIndex) throws SQLException { + return decrypt(resultSet.getString(columnIndex)); + } + + @Override + public String getResult(CallableStatement callableStatement, int columnIndex) throws SQLException { + return decrypt(callableStatement.getString(columnIndex)); + } + + private String decrypt(String result) { + if (StringUtils.isNotBlank(result)) { + String decrypt = AESUtils.decrypt(result); + return Objects.nonNull(decrypt) ? decrypt : result; + } + return result; + } +} diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/tool/AESUtils.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/tool/AESUtils.java new file mode 100644 index 00000000..9e14e089 --- /dev/null +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/tool/AESUtils.java @@ -0,0 +1,80 @@ +package com.autohome.frostmourne.monitor.tool; + +import com.autohome.frostmourne.monitor.config.properties.EncryptProperties; +import org.apache.commons.codec.binary.Base64; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.*; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +/** + * AES utils + * + * @author limobo + * @since 2022/9/9 14:48 + */ +public final class AESUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(AESUtils.class); + + private static final String DEFAULT_CIPHER_ALGORITHM = "SHA1PRNG"; + private static final String KEY_ALGORITHM = "AES"; + + public static String encrypt(String data) { + try { + return doAES(data, Cipher.ENCRYPT_MODE); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { + LOGGER.error("encrypt error with data: {}", data, e); + } catch (Exception e) { + LOGGER.error("encrypt exception with data: {}", data, e); + } + return null; + } + + public static String decrypt(String data) { + try { + return doAES(data, Cipher.DECRYPT_MODE); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { + LOGGER.error("decrypt error with data: {}", data, e); + } catch (Exception e) { + LOGGER.error("decrypt exception with data: {}", data, e); + } + return null; + } + + private static String doAES(String data, int mode) + throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + + boolean encrypt = mode == Cipher.ENCRYPT_MODE; + byte[] content; + if (encrypt) { + content = data.getBytes(StandardCharsets.UTF_8); + } else { + content = Base64.decodeBase64(data.getBytes(StandardCharsets.UTF_8)); + } + + String key = EncryptProperties.getInstance().getKey(); + + KeyGenerator generator = KeyGenerator.getInstance(KEY_ALGORITHM); + SecureRandom secureRandom = SecureRandom.getInstance(DEFAULT_CIPHER_ALGORITHM); + secureRandom.setSeed(key.getBytes(StandardCharsets.UTF_8)); + generator.init(128, secureRandom); + SecretKeySpec keySpec = new SecretKeySpec(generator.generateKey().getEncoded(), KEY_ALGORITHM); + + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(mode, keySpec); + byte[] result = cipher.doFinal(content); + + if (encrypt) { + return Base64.encodeBase64String(result); + } else { + return new String(result, StandardCharsets.UTF_8); + } + } + +} diff --git a/frostmourne-monitor/src/main/resources/application.properties b/frostmourne-monitor/src/main/resources/application.properties index 80d76cf3..b2cde399 100644 --- a/frostmourne-monitor/src/main/resources/application.properties +++ b/frostmourne-monitor/src/main/resources/application.properties @@ -1,6 +1,7 @@ server.port=10054 logging.config=classpath:log4j2/log4j2.xml + initial.password=${initial_password:} spring.freemarker.checkTemplateLocation=false @@ -54,3 +55,9 @@ wechat.secret=${your.wechat.secret:} schedule.enabled=${schedule_enabled:true} schedule.trigger.pool.fast.max=200 schedule.trigger.pool.slow.max=200 + +### 加解密相关配置 +# 密钥,一旦设置不要轻易修改 +encrypt.key=${encrypt_key:EX31$@*^ac1} +# 敏感字段 +encrypt.sensitives=${encrypt_sensitives:username,password} From 536df8325dbf40b97fa8d082919e5c83c77509b4 Mon Sep 17 00:00:00 2001 From: limbo Date: Thu, 15 Sep 2022 16:19:01 +0800 Subject: [PATCH 2/9] get jdbc type. --- .../frostmourne/monitor/dao/jdbc/DataSourceJdbcType.java | 6 ++++++ .../monitor/dao/jdbc/impl/DataSourceJdbcManager.java | 9 +-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/jdbc/DataSourceJdbcType.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/jdbc/DataSourceJdbcType.java index 7caf8012..f60b2c5f 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/jdbc/DataSourceJdbcType.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/jdbc/DataSourceJdbcType.java @@ -1,5 +1,7 @@ package com.autohome.frostmourne.monitor.dao.jdbc; +import com.autohome.frostmourne.monitor.model.enums.DataSourceType; + public enum DataSourceJdbcType { /** @@ -32,4 +34,8 @@ public String getDriverClassName() { return driverClassName; } + public static DataSourceJdbcType fromDataSourceType(DataSourceType dataSourceType) { + return DataSourceJdbcType.valueOf(dataSourceType.name().toUpperCase()); + } + } diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/jdbc/impl/DataSourceJdbcManager.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/jdbc/impl/DataSourceJdbcManager.java index 0e44fa32..b71132be 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/jdbc/impl/DataSourceJdbcManager.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/jdbc/impl/DataSourceJdbcManager.java @@ -6,7 +6,6 @@ import javax.sql.DataSource; -import com.autohome.frostmourne.monitor.model.enums.DataSourceType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -97,13 +96,7 @@ private void closeDataSource(DruidDataSource dataSource) { private DruidDataSource createDataSource(DataSourceContract dataSourceContract) throws SQLException { DruidDataSource dataSource = new DruidDataSource(); - if (DataSourceType.clickhouse.equals(dataSourceContract.getDatasourceType())) { - dataSource.setDriverClassName(DataSourceJdbcType.CLICKHOUSE.getDriverClassName()); - } else if (DataSourceType.mysql.equals(dataSourceContract.getDatasourceType())) { - dataSource.setDriverClassName(DataSourceJdbcType.MYSQL.getDriverClassName()); - } else if (DataSourceType.sqlserver.equals(dataSourceContract.getDatasourceType())) { - dataSource.setDriverClassName(DataSourceJdbcType.SQLSERVER.getDriverClassName()); - } + dataSource.setDriverClassName(DataSourceJdbcType.fromDataSourceType(dataSourceContract.getDatasourceType()).getDriverClassName()); dataSource.setUrl(dataSourceContract.getServiceAddress()); dataSource.setUsername(dataSourceContract.getSettings().get("username")); dataSource.setPassword(dataSourceContract.getSettings().get("password")); From 6fc89d3cefa2cad6b4a9b08300ee6b233ed8ae96 Mon Sep 17 00:00:00 2001 From: limbo Date: Thu, 15 Sep 2022 16:21:55 +0800 Subject: [PATCH 3/9] handle sensitive field (username,password) --- .../domain/generate/DataSource.java | 15 ++++++++ .../dynamic/DataSourceDynamicMapper.java | 4 +- .../dynamic/DataSourceDynamicSqlSupport.java | 2 +- .../model/contract/DataSourceContract.java | 15 ++++++++ .../service/admin/impl/DataAdminService.java | 37 +++++++++++++++++-- .../transform/DataSourceTransformer.java | 27 +++++++++++++- .../resources/application-local.properties | 8 ++++ 7 files changed, 102 insertions(+), 6 deletions(-) diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/mybatis/frostmourne/domain/generate/DataSource.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/mybatis/frostmourne/domain/generate/DataSource.java index b09caeea..c2281915 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/mybatis/frostmourne/domain/generate/DataSource.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/mybatis/frostmourne/domain/generate/DataSource.java @@ -167,4 +167,19 @@ public int hashCode() { result = prime * result + ((getProperties() == null) ? 0 : getProperties().hashCode()); return result; } + + @Override + public String toString() { + return "DataSource{" + + "id=" + id + + ", datasourceName='" + datasourceName + '\'' + + ", datasourceType=" + datasourceType + + ", serviceAddress='" + serviceAddress + '\'' + + ", creator='" + creator + '\'' + + ", createAt=" + createAt + + ", modifier='" + modifier + '\'' + + ", modifyAt=" + modifyAt + + ", properties='" + properties + '\'' + + '}'; + } } \ No newline at end of file diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/mybatis/frostmourne/mapper/dynamic/DataSourceDynamicMapper.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/mybatis/frostmourne/mapper/dynamic/DataSourceDynamicMapper.java index 1865b37a..57b49858 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/mybatis/frostmourne/mapper/dynamic/DataSourceDynamicMapper.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/mybatis/frostmourne/mapper/dynamic/DataSourceDynamicMapper.java @@ -6,6 +6,8 @@ import com.autohome.frostmourne.monitor.dao.mybatis.frostmourne.domain.generate.DataSource; import java.util.List; import java.util.Optional; + +import com.autohome.frostmourne.monitor.handler.CryptoTypeHandler; import org.apache.ibatis.annotations.DeleteProvider; import org.apache.ibatis.annotations.InsertProvider; import org.apache.ibatis.annotations.Mapper; @@ -58,7 +60,7 @@ public interface DataSourceDynamicMapper { @Result(column="create_at", property="createAt", jdbcType=JdbcType.TIMESTAMP), @Result(column="modifier", property="modifier", jdbcType=JdbcType.VARCHAR), @Result(column="modify_at", property="modifyAt", jdbcType=JdbcType.TIMESTAMP), - @Result(column="properties", property="properties", jdbcType=JdbcType.LONGVARCHAR) + @Result(column="properties", property="properties", jdbcType=JdbcType.LONGVARCHAR, typeHandler = CryptoTypeHandler.class) }) List selectMany(SelectStatementProvider selectStatement); diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/mybatis/frostmourne/mapper/dynamic/DataSourceDynamicSqlSupport.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/mybatis/frostmourne/mapper/dynamic/DataSourceDynamicSqlSupport.java index 77d8ed6e..e5c19f25 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/mybatis/frostmourne/mapper/dynamic/DataSourceDynamicSqlSupport.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/dao/mybatis/frostmourne/mapper/dynamic/DataSourceDynamicSqlSupport.java @@ -71,7 +71,7 @@ public static final class DataSource extends SqlTable { public final SqlColumn modifyAt = column("modify_at", JDBCType.TIMESTAMP); - public final SqlColumn properties = column("properties", JDBCType.LONGVARCHAR); + public final SqlColumn properties = column("properties", JDBCType.LONGVARCHAR, "com.autohome.frostmourne.monitor.handler.CryptoTypeHandler"); public DataSource() { super("data_source"); diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/contract/DataSourceContract.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/contract/DataSourceContract.java index 6b1c0ce2..a78b8e8f 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/contract/DataSourceContract.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/contract/DataSourceContract.java @@ -96,4 +96,19 @@ public Date getModifyAt() { public void setModifyAt(Date modifyAt) { this.modifyAt = modifyAt; } + + @Override + public String toString() { + return "DataSourceContract{" + + "id=" + id + + ", datasourceName='" + datasourceName + '\'' + + ", datasourceType=" + datasourceType + + ", serviceAddress='" + serviceAddress + '\'' + + ", settings=" + settings + + ", creator='" + creator + '\'' + + ", createAt=" + createAt + + ", modifier='" + modifier + '\'' + + ", modifyAt=" + modifyAt + + '}'; + } } diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/DataAdminService.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/DataAdminService.java index 530945dd..c1d0dd3d 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/DataAdminService.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/DataAdminService.java @@ -11,7 +11,10 @@ import javax.annotation.Resource; +import com.autohome.frostmourne.monitor.config.properties.EncryptProperties; import com.autohome.frostmourne.monitor.model.enums.DataSourceType; +import com.autohome.frostmourne.monitor.model.vo.DataSourceVO; +import com.autohome.frostmourne.monitor.tool.AESUtils; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -69,9 +72,15 @@ public boolean saveDataSource(String account, DataSourceContract dataSourceContr dataSource.setModifier(account); dataSource.setServiceAddress(dataSourceContract.getServiceAddress()); dataSource.setModifyAt(new Date()); + if (dataSourceContract.getSettings() != null && dataSourceContract.getSettings().size() > 0) { + // 解密操作 + Map settings = handleEncryptSetting(dataSourceContract); + // 更新settings + dataSourceContract.setSettings(settings); dataSource.setProperties(JacksonUtil.serialize(dataSourceContract.getSettings())); } + if (dataSourceContract.getId() != null && dataSourceContract.getId() > 0) { dataSource.setId(dataSourceContract.getId()); if (DataSourceType.elasticsearch.equals(dataSource.getDatasourceType())) { @@ -90,7 +99,6 @@ public boolean saveDataSource(String account, DataSourceContract dataSourceContr dataSource.setCreator(account); dataSource.setCreateAt(new Date()); return dataSourceRepository.insert(dataSource) > 0; - } @Override @@ -134,7 +142,12 @@ public List dataOptions() { Map> dataOptionMap = new HashMap<>(); for (DataSource dataSource : dataSourceList) { DataSourceOption dataSourceOption = new DataSourceOption(); - dataSourceOption.setDataSource(dataSource); + DataSourceVO dataSourceVO = DataSourceVO.builder() + .id(dataSource.getId()) + .datasourceName(dataSource.getDatasourceName()) + .datasourceType(dataSource.getDatasourceType()) + .build(); + dataSourceOption.setDataSourceVO(dataSourceVO); dataSourceOption.setDataNameContractList(dataNameList.stream().filter(dataName -> dataName.getDataSourceId().equals(dataSource.getId())) .map(DataAdminService::toDataNameContract).collect(Collectors.toList())); if (dataOptionMap.containsKey(dataSource.getDatasourceType().name())) { @@ -184,7 +197,7 @@ private List parseTreeDataOptionByDataSourceOptions(List { - TreeDataOption option = new TreeDataOption(String.valueOf(item.getDataSource().getId()), item.getDataSource().getDatasourceName()); + TreeDataOption option = new TreeDataOption(String.valueOf(item.getDataSourceVO().getId()), item.getDataSourceVO().getDatasourceName()); option.setChildren(this.parseTreeDataOptionByDataNameContracts(item.getDataNameContractList())); return option; }).collect(Collectors.toList()); @@ -276,4 +289,22 @@ static DataNameContract toDataNameContract(DataName dataName) { return dataNameContract; } + + private Map handleEncryptSetting(DataSourceContract dataSourceContract) { + Map settings = dataSourceContract.getSettings(); + List sensitiveFields = EncryptProperties.getInstance().getSensitiveFields(); + if (!CollectionUtils.isEmpty(sensitiveFields)) { + sensitiveFields.forEach(field -> { + if (settings.containsKey(field)) { + // 判断是否是加密数据, 并更新原键值 + String decrypt = AESUtils.decrypt(settings.get(field)); + if (decrypt != null) { + settings.put(field, decrypt); + } + } + }); + } + return settings; + + } } diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/transform/DataSourceTransformer.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/transform/DataSourceTransformer.java index 27ca306f..508a0267 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/transform/DataSourceTransformer.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/transform/DataSourceTransformer.java @@ -1,17 +1,25 @@ package com.autohome.frostmourne.monitor.transform; import java.util.HashMap; +import java.util.List; import java.util.Map; +import com.autohome.frostmourne.monitor.config.properties.EncryptProperties; import com.autohome.frostmourne.monitor.dao.mybatis.frostmourne.domain.generate.DataSource; +import com.autohome.frostmourne.monitor.tool.AESUtils; import org.elasticsearch.common.Strings; import com.autohome.frostmourne.common.jackson.JacksonUtil; import com.autohome.frostmourne.monitor.model.contract.DataSourceContract; import com.fasterxml.jackson.core.type.TypeReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; public class DataSourceTransformer { + private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceTransformer.class); + public static DataSourceContract model2Contract(DataSource dataSource) { DataSourceContract dataSourceContract = new DataSourceContract(); dataSourceContract.setDatasourceName(dataSource.getDatasourceName()); @@ -19,7 +27,17 @@ public static DataSourceContract model2Contract(DataSource dataSource) { dataSourceContract.setId(dataSource.getId()); dataSourceContract.setServiceAddress(dataSource.getServiceAddress()); if (!Strings.isNullOrEmpty(dataSource.getProperties())) { - dataSourceContract.setSettings(JacksonUtil.deSerialize(dataSource.getProperties(), new TypeReference>() {})); + Map settings = new HashMap<>(); + try { + settings = JacksonUtil.deSerialize(dataSource.getProperties(), new TypeReference>() {}); + } catch (Exception e) { + // 当加密的key(配置 encrypt.key)发生变更后会出现原来已加密的字符串无法解密,原样输出则json解析失败 + // 此时设置一个空的 settings,能够在"数据源"页面重新设置账号密码等信息,新的key会重新加密 + LOGGER.error("dataSource properties json parse error, datasource: {}", dataSource, e); + } + // 加密敏感字段 + handleEncryptSettings(settings); + dataSourceContract.setSettings(settings); } else { dataSourceContract.setSettings(new HashMap<>()); } @@ -29,4 +47,11 @@ public static DataSourceContract model2Contract(DataSource dataSource) { dataSourceContract.setModifyAt(dataSource.getModifyAt()); return dataSourceContract; } + + private static void handleEncryptSettings(Map settings) { + List sensitiveFields = EncryptProperties.getInstance().getSensitiveFields(); + if (!CollectionUtils.isEmpty(sensitiveFields)) { + sensitiveFields.forEach(field -> settings.computeIfPresent(field, (k, v) -> AESUtils.encrypt(v))); + } + } } diff --git a/frostmourne-monitor/src/main/resources/application-local.properties b/frostmourne-monitor/src/main/resources/application-local.properties index 77d21529..55c979ec 100644 --- a/frostmourne-monitor/src/main/resources/application-local.properties +++ b/frostmourne-monitor/src/main/resources/application-local.properties @@ -1,3 +1,7 @@ +### debug +logging.level.com.autohome.frostmourne.monitor: debug +logging.level.com.autohome.frostmourne.monitor.schedule: info + ### initial login password initial_password=123456 @@ -35,3 +39,7 @@ your.wechat.agentid= your.wechat.secret= schedule_enabled=true + +### encryption +encrypt_key:EX31$@*^ac1 +encrypt_sensitives:username,password From 9a54942294eeb566261d875ccfc0ce48f7ad16bc Mon Sep 17 00:00:00 2001 From: limbo Date: Thu, 15 Sep 2022 16:22:51 +0800 Subject: [PATCH 4/9] hide datasource entity. --- .../monitor/model/contract/DataOption.java | 19 ++--------- .../model/contract/DataSourceOption.java | 20 +++--------- .../monitor/model/vo/DataSourceVO.java | 32 +++++++++++++++++++ frostmourne-vue/src/views/alarm/edit.vue | 4 +-- 4 files changed, 41 insertions(+), 34 deletions(-) create mode 100644 frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/vo/DataSourceVO.java diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/contract/DataOption.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/contract/DataOption.java index 9692e765..24d339cb 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/contract/DataOption.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/contract/DataOption.java @@ -1,26 +1,13 @@ package com.autohome.frostmourne.monitor.model.contract; +import lombok.Data; + import java.util.List; +@Data public class DataOption { private String datasourceType; private List dataSourceOptionList; - - public String getDatasourceType() { - return datasourceType; - } - - public void setDatasourceType(String datasourceType) { - this.datasourceType = datasourceType; - } - - public List getDataSourceOptionList() { - return dataSourceOptionList; - } - - public void setDataSourceOptionList(List dataSourceOptionList) { - this.dataSourceOptionList = dataSourceOptionList; - } } diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/contract/DataSourceOption.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/contract/DataSourceOption.java index a2df165b..764a5f86 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/contract/DataSourceOption.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/contract/DataSourceOption.java @@ -3,26 +3,14 @@ import java.util.List; import com.autohome.frostmourne.monitor.dao.mybatis.frostmourne.domain.generate.DataSource; +import com.autohome.frostmourne.monitor.model.vo.DataSourceVO; +import lombok.Data; +@Data public class DataSourceOption { - private DataSource dataSource; + private DataSourceVO dataSourceVO; private List dataNameContractList; - public DataSource getDataSource() { - return dataSource; - } - - public void setDataSource(DataSource dataSource) { - this.dataSource = dataSource; - } - - public List getDataNameContractList() { - return dataNameContractList; - } - - public void setDataNameContractList(List dataNameContractList) { - this.dataNameContractList = dataNameContractList; - } } diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/vo/DataSourceVO.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/vo/DataSourceVO.java new file mode 100644 index 00000000..f46c0faa --- /dev/null +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/vo/DataSourceVO.java @@ -0,0 +1,32 @@ +package com.autohome.frostmourne.monitor.model.vo; + +import com.autohome.frostmourne.monitor.model.enums.DataSourceType; +import lombok.Builder; +import lombok.Data; + +/** + * description + * + * @author fangpeng + * @since 2022/9/14 18:55 + */ +@Data +@Builder +public class DataSourceVO { + + /** + * 主键 + */ + private Long id; + + /** + * 数据源名称 + */ + private String datasourceName; + + /** + * 数据源类型。(Elasticsearch, Influxdb) + */ + private DataSourceType datasourceType; + +} diff --git a/frostmourne-vue/src/views/alarm/edit.vue b/frostmourne-vue/src/views/alarm/edit.vue index 23f1867b..145d3e1a 100644 --- a/frostmourne-vue/src/views/alarm/edit.vue +++ b/frostmourne-vue/src/views/alarm/edit.vue @@ -977,8 +977,8 @@ export default { } for (var j = 0; j < remoteOptions[i].dataSourceOptionList.length; j++) { var dataSource = { - label: remoteOptions[i].dataSourceOptionList[j].dataSource.datasourceName, - value: remoteOptions[i].dataSourceOptionList[j].dataSource.id, + label: remoteOptions[i].dataSourceOptionList[j].dataSourceVO.datasourceName, + value: remoteOptions[i].dataSourceOptionList[j].dataSourceVO.id, children: [] } From b78736c8fdba6d223441ec52b30728f610a388e1 Mon Sep 17 00:00:00 2001 From: limbo Date: Thu, 15 Sep 2022 16:23:53 +0800 Subject: [PATCH 5/9] integrate lombok. --- pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pom.xml b/pom.xml index a190e6df..c25d9a56 100644 --- a/pom.xml +++ b/pom.xml @@ -80,6 +80,14 @@ + + + + org.projectlombok + lombok + provided + + From 7bae1f4755967f9a141ab7f7743911ccd30a5999 Mon Sep 17 00:00:00 2001 From: limbo Date: Fri, 16 Sep 2022 15:32:40 +0800 Subject: [PATCH 6/9] refactor padAlarm method. --- .../service/admin/impl/AlarmAdminService.java | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/AlarmAdminService.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/AlarmAdminService.java index a45026f8..0945a6e6 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/AlarmAdminService.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/AlarmAdminService.java @@ -467,25 +467,19 @@ private void saveMetric(MetricContract metricContract, Long alarmId, Long ruleId @Override public void padAlarm(AlarmContract alarmContract) { - alarmContract.setAlarmType(alarmContract.getMetricContract().getDataName()); + String dataNameStr = alarmContract.getMetricContract().getDataName(); + alarmContract.setAlarmType(dataNameStr); String ruleType = metricRuleMap.get(alarmContract.getMetricContract().getMetricType()); alarmContract.getRuleContract().setRuleType(ruleType); - if (alarmContract.getMetricContract().getDataName().equalsIgnoreCase("http")) { - return; - } - if (alarmContract.getMetricContract().getDataName().equalsIgnoreCase("ping")) { - return; - } - if (alarmContract.getMetricContract().getDataName().equalsIgnoreCase("telnet")) { + if (dataNameStr.equalsIgnoreCase("http") + || dataNameStr.equalsIgnoreCase("ping") + || dataNameStr.equalsIgnoreCase("telnet")) { return; } - Optional optionalDataName = dataNameRepository.findByName(alarmContract.getMetricContract().getDataName()); - if (!optionalDataName.isPresent()) { - throw new ProtocolException(1290, "dataName not exist. " + alarmContract.getMetricContract().getDataName()); - } - DataName dataName = optionalDataName.get(); + Optional optionalDataName = dataNameRepository.findByName(dataNameStr); + DataName dataName = optionalDataName.orElseThrow(() -> new ProtocolException(1290, "dataName not exist. " + dataNameStr)); alarmContract.getMetricContract().setDataNameId(dataName.getId()); alarmContract.getMetricContract().setDataNameContract(DataAdminService.toDataNameContract(dataName)); alarmContract.getMetricContract().setDataSourceId(dataName.getDataSourceId()); @@ -494,6 +488,5 @@ public void padAlarm(AlarmContract alarmContract) { DataSourceContract dataSourceContract = optionalDataSource.map(DataSourceTransformer::model2Contract) .orElseThrow(() -> new ProtocolException(1900, "dataSource not exist. id: " + dataName.getDataSourceId())); alarmContract.getMetricContract().setDataSourceContract(dataSourceContract); - } } From bbbe8d5ae985d94cb97a87ed75f74cb0938c7752 Mon Sep 17 00:00:00 2001 From: limbo Date: Fri, 16 Sep 2022 15:34:08 +0800 Subject: [PATCH 7/9] Add controls for whether to encrypt. --- .../service/admin/impl/DataAdminService.java | 7 +++++-- .../monitor/transform/DataSourceTransformer.java | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/DataAdminService.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/DataAdminService.java index c1d0dd3d..8d1264e0 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/DataAdminService.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/DataAdminService.java @@ -114,8 +114,11 @@ public boolean removeDataSource(Long id) { public PagerContract findDatasource(int pageIndex, int pageSize, DataSourceType datasourceType) { Page page = PageHelper.startPage(pageIndex, pageSize); List list = this.dataSourceRepository.find(datasourceType); - return new PagerContract<>(list.stream().map(DataSourceTransformer::model2Contract).collect(Collectors.toList()), page.getPageSize(), page.getPageNum(), - (int)page.getTotal()); + return new PagerContract<>( + list.stream() + .map(i -> DataSourceTransformer.model2Contract(i, true)) + .collect(Collectors.toList()), + page.getPageSize(), page.getPageNum(), (int)page.getTotal()); } @Override diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/transform/DataSourceTransformer.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/transform/DataSourceTransformer.java index 508a0267..3f856a85 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/transform/DataSourceTransformer.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/transform/DataSourceTransformer.java @@ -21,6 +21,17 @@ public class DataSourceTransformer { private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceTransformer.class); public static DataSourceContract model2Contract(DataSource dataSource) { + return model2Contract(dataSource, false); + } + + /** + * Encrypt DataSource sensitive fields, like username, password. + * + * @param dataSource dataSource entity + * @param encrypt whether to encrypt + * @return {@link DataSourceContract} + */ + public static DataSourceContract model2Contract(DataSource dataSource, boolean encrypt) { DataSourceContract dataSourceContract = new DataSourceContract(); dataSourceContract.setDatasourceName(dataSource.getDatasourceName()); dataSourceContract.setDatasourceType(dataSource.getDatasourceType()); @@ -36,7 +47,9 @@ public static DataSourceContract model2Contract(DataSource dataSource) { LOGGER.error("dataSource properties json parse error, datasource: {}", dataSource, e); } // 加密敏感字段 - handleEncryptSettings(settings); + if (encrypt) { + handleEncryptSettings(settings); + } dataSourceContract.setSettings(settings); } else { dataSourceContract.setSettings(new HashMap<>()); From 5cc4df1c008f335a23df2d3534c96592b99c7969 Mon Sep 17 00:00:00 2001 From: limbo Date: Fri, 16 Sep 2022 16:28:36 +0800 Subject: [PATCH 8/9] Encrypt sensitive api data exposed to the webUI. --- .../monitor/controller/AdminController.java | 12 ++++++++++++ .../service/admin/impl/AlarmAdminService.java | 5 +---- .../frostmourne/monitor/tool/AESUtils.java | 14 ++++++++++++++ .../monitor/transform/DataSourceTransformer.java | 8 +------- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/controller/AdminController.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/controller/AdminController.java index b2d8b05b..e622fd56 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/controller/AdminController.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/controller/AdminController.java @@ -4,6 +4,7 @@ import com.autohome.frostmourne.monitor.dao.mybatis.frostmourne.domain.generate.Alarm; import com.autohome.frostmourne.monitor.model.enums.AlarmStatus; +import com.autohome.frostmourne.monitor.tool.AESUtils; import org.springframework.web.bind.annotation.*; import com.autohome.frostmourne.common.contract.PagerContract; @@ -12,6 +13,9 @@ import com.autohome.frostmourne.monitor.service.admin.IAlarmAdminService; import com.autohome.frostmourne.monitor.tool.AuthTool; +import java.util.Map; +import java.util.Objects; + @RestController @RequestMapping(value = {"/admin", "/api/monitor-api/admin"}) public class AdminController { @@ -58,6 +62,13 @@ public Protocol findById(@RequestParam(value = "alarmId") Long al if (alarmContract == null) { return new Protocol<>(404, "监控不存在"); } + + // 敏感信息加密 + Map settings = alarmContract.getMetricContract().getDataSourceContract().getSettings(); + if (Objects.nonNull(settings)) { + AESUtils.encryptMappingSensitive(settings); + alarmContract.getMetricContract().getDataSourceContract().setSettings(settings); + } return new Protocol<>(alarmContract); } @@ -66,4 +77,5 @@ public Protocol> list(int pageIndex, int pageSize, Long ala PagerContract pagerContract = this.alarmAdminService.find(pageIndex, pageSize, alarmId, name, teamName, status, serviceId); return new Protocol<>(pagerContract); } + } diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/AlarmAdminService.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/AlarmAdminService.java index 0945a6e6..bd457a7f 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/AlarmAdminService.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/service/admin/impl/AlarmAdminService.java @@ -146,10 +146,7 @@ public boolean close(Long alarmId) { public AlarmContract findById(Long alarmId) { AlarmContract alarmContract = new AlarmContract(); Optional optionalAlarm = alarmRepository.selectByPrimaryKey(alarmId); - if (!optionalAlarm.isPresent()) { - return null; - } - Alarm alarm = optionalAlarm.get(); + Alarm alarm = optionalAlarm.orElseThrow(() -> new ProtocolException(404, String.format("The alarm[id=%s] not exists", alarmId))); alarmContract.setId(alarmId); alarmContract.setStatus(alarm.getStatus()); alarmContract.setOwnerKey(alarm.getOwnerKey()); diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/tool/AESUtils.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/tool/AESUtils.java index 9e14e089..2ec8ad4f 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/tool/AESUtils.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/tool/AESUtils.java @@ -4,6 +4,7 @@ import org.apache.commons.codec.binary.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; import javax.crypto.*; import javax.crypto.spec.SecretKeySpec; @@ -11,6 +12,8 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.util.List; +import java.util.Map; /** * AES utils @@ -77,4 +80,15 @@ private static String doAES(String data, int mode) } } + public static void encryptMappingSensitive(Map settings) { + try { + List sensitiveFields = EncryptProperties.getInstance().getSensitiveFields(); + if (!CollectionUtils.isEmpty(sensitiveFields)) { + sensitiveFields.forEach(field -> settings.computeIfPresent(field, (k, v) -> encrypt(v))); + } + } catch (Exception e) { + LOGGER.error("encryptMappingSensitive error", e); + } + } + } diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/transform/DataSourceTransformer.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/transform/DataSourceTransformer.java index 3f856a85..8ef7be1d 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/transform/DataSourceTransformer.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/transform/DataSourceTransformer.java @@ -48,7 +48,7 @@ public static DataSourceContract model2Contract(DataSource dataSource, boolean e } // 加密敏感字段 if (encrypt) { - handleEncryptSettings(settings); + AESUtils.encryptMappingSensitive(settings); } dataSourceContract.setSettings(settings); } else { @@ -61,10 +61,4 @@ public static DataSourceContract model2Contract(DataSource dataSource, boolean e return dataSourceContract; } - private static void handleEncryptSettings(Map settings) { - List sensitiveFields = EncryptProperties.getInstance().getSensitiveFields(); - if (!CollectionUtils.isEmpty(sensitiveFields)) { - sensitiveFields.forEach(field -> settings.computeIfPresent(field, (k, v) -> AESUtils.encrypt(v))); - } - } } From cee2189ad28e2f75f70dc727279bd0d86fadc2f4 Mon Sep 17 00:00:00 2001 From: limbo Date: Fri, 16 Sep 2022 16:53:41 +0800 Subject: [PATCH 9/9] update comment. --- .../com/autohome/frostmourne/monitor/model/vo/DataSourceVO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/vo/DataSourceVO.java b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/vo/DataSourceVO.java index f46c0faa..5747f271 100644 --- a/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/vo/DataSourceVO.java +++ b/frostmourne-monitor/src/main/java/com/autohome/frostmourne/monitor/model/vo/DataSourceVO.java @@ -7,7 +7,7 @@ /** * description * - * @author fangpeng + * @author limbo * @since 2022/9/14 18:55 */ @Data