mirror of
https://github.com/alibaba/canal.git
synced 2026-11-04 00:30:15 +08:00
修复rest etl 存在SQL注入风险 (#1685)
* kafka 模式默认开启事务异步发送 * fix adapter etl rest sql inject bug
This commit is contained in:
@@ -17,7 +17,6 @@ import org.elasticsearch.client.transport.TransportClient;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.transport.client.PreBuiltTransportClient;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.otter.canal.client.adapter.OuterAdapter;
|
||||
@@ -30,12 +29,7 @@ import com.alibaba.otter.canal.client.adapter.es.monitor.ESConfigMonitor;
|
||||
import com.alibaba.otter.canal.client.adapter.es.service.ESEtlService;
|
||||
import com.alibaba.otter.canal.client.adapter.es.service.ESSyncService;
|
||||
import com.alibaba.otter.canal.client.adapter.es.support.ESTemplate;
|
||||
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
||||
import com.alibaba.otter.canal.client.adapter.support.Dml;
|
||||
import com.alibaba.otter.canal.client.adapter.support.EtlResult;
|
||||
import com.alibaba.otter.canal.client.adapter.support.OuterAdapterConfig;
|
||||
import com.alibaba.otter.canal.client.adapter.support.SPI;
|
||||
|
||||
import com.alibaba.otter.canal.client.adapter.support.*;
|
||||
|
||||
/**
|
||||
* ES外部适配器
|
||||
@@ -194,7 +188,6 @@ public class ESAdapter implements OuterAdapter {
|
||||
} else {
|
||||
StringBuilder resultMsg = new StringBuilder();
|
||||
boolean resSuccess = true;
|
||||
// ds不为空说明传入的是datasourceKey
|
||||
for (ESSyncConfig configTmp : esSyncConfig.values()) {
|
||||
// 取所有的destination为task的配置
|
||||
if (configTmp.getDestination().equals(task)) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.alibaba.otter.canal.client.adapter.es.config;
|
||||
|
||||
import com.alibaba.otter.canal.client.adapter.support.AdapterConfig;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -11,7 +13,7 @@ import java.util.Map;
|
||||
* @author rewerma 2018-11-01
|
||||
* @version 1.0.0
|
||||
*/
|
||||
public class ESSyncConfig {
|
||||
public class ESSyncConfig implements AdapterConfig {
|
||||
|
||||
private String dataSourceKey; // 数据源key
|
||||
|
||||
@@ -78,7 +80,11 @@ public class ESSyncConfig {
|
||||
this.esMapping = esMapping;
|
||||
}
|
||||
|
||||
public static class ESMapping {
|
||||
public ESMapping getMapping(){
|
||||
return esMapping;
|
||||
}
|
||||
|
||||
public static class ESMapping implements AdapterMapping{
|
||||
|
||||
private String _index;
|
||||
private String _type;
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
package com.alibaba.otter.canal.client.adapter.es.service;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -25,18 +20,15 @@ import org.elasticsearch.client.transport.TransportClient;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
||||
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig.ESMapping;
|
||||
import com.alibaba.otter.canal.client.adapter.es.config.SchemaItem.FieldItem;
|
||||
import com.alibaba.otter.canal.client.adapter.es.support.ESTemplate;
|
||||
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
||||
import com.alibaba.otter.canal.client.adapter.support.AbstractEtlService;
|
||||
import com.alibaba.otter.canal.client.adapter.support.AdapterConfig;
|
||||
import com.alibaba.otter.canal.client.adapter.support.EtlResult;
|
||||
import com.alibaba.otter.canal.client.adapter.support.Util;
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
/**
|
||||
* ES ETL Service
|
||||
@@ -44,116 +36,24 @@ import com.google.common.base.Joiner;
|
||||
* @author rewerma 2018-11-01
|
||||
* @version 1.0.0
|
||||
*/
|
||||
public class ESEtlService {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ESEtlService.class);
|
||||
public class ESEtlService extends AbstractEtlService {
|
||||
|
||||
private TransportClient transportClient;
|
||||
private ESTemplate esTemplate;
|
||||
private ESSyncConfig config;
|
||||
|
||||
public ESEtlService(TransportClient transportClient, ESSyncConfig config){
|
||||
super("ES", config);
|
||||
this.transportClient = transportClient;
|
||||
this.esTemplate = new ESTemplate(transportClient);
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public EtlResult importData(List<String> params) {
|
||||
EtlResult etlResult = new EtlResult();
|
||||
AtomicLong impCount = new AtomicLong();
|
||||
List<String> errMsg = new ArrayList<>();
|
||||
String esIndex = "";
|
||||
if (config == null) {
|
||||
logger.warn("esSycnCofnig is null, etl go end !");
|
||||
etlResult.setErrorMessage("esSycnCofnig is null, etl go end !");
|
||||
return etlResult;
|
||||
}
|
||||
|
||||
ESMapping mapping = config.getEsMapping();
|
||||
|
||||
esIndex = mapping.get_index();
|
||||
DruidDataSource dataSource = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
||||
Pattern pattern = Pattern.compile(".*:(.*)://.*/(.*)\\?.*$");
|
||||
Matcher matcher = pattern.matcher(dataSource.getUrl());
|
||||
if (!matcher.find()) {
|
||||
throw new RuntimeException("Not found the schema of jdbc-url: " + config.getDataSourceKey());
|
||||
}
|
||||
String schema = matcher.group(2);
|
||||
|
||||
logger.info("etl from db: {}, to es index: {}", schema, esIndex);
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
String sql = mapping.getSql();
|
||||
|
||||
// 拼接条件
|
||||
if (mapping.getEtlCondition() != null && params != null) {
|
||||
String etlCondition = mapping.getEtlCondition();
|
||||
int size = params.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
etlCondition = etlCondition.replace("{" + i + "}", params.get(i));
|
||||
}
|
||||
|
||||
sql += " " + etlCondition;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("etl sql : {}", mapping.getSql());
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
String countSql = "SELECT COUNT(1) FROM ( " + sql + ") _CNT ";
|
||||
long cnt = (Long) Util.sqlRS(dataSource, countSql, rs -> {
|
||||
Long count = null;
|
||||
try {
|
||||
if (rs.next()) {
|
||||
count = ((Number) rs.getObject(1)).longValue();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
return count == null ? 0L : count;
|
||||
});
|
||||
|
||||
// 当大于1万条记录时开启多线程
|
||||
if (cnt >= 10000) {
|
||||
int threadCount = 3; // 从配置读取默认为3
|
||||
long perThreadCnt = cnt / threadCount;
|
||||
ExecutorService executor = Util.newFixedThreadPool(threadCount, 5000L);
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
long offset = i * perThreadCnt;
|
||||
Long size = null;
|
||||
if (i != threadCount - 1) {
|
||||
size = perThreadCnt;
|
||||
}
|
||||
String sqlFinal;
|
||||
if (size != null) {
|
||||
sqlFinal = sql + " LIMIT " + offset + "," + size;
|
||||
} else {
|
||||
sqlFinal = sql + " LIMIT " + offset + "," + cnt;
|
||||
}
|
||||
executor.execute(() -> executeSqlImport(dataSource, sqlFinal, mapping, impCount, errMsg));
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
while (!executor.awaitTermination(3, TimeUnit.SECONDS)) {
|
||||
// ignore
|
||||
}
|
||||
} else {
|
||||
executeSqlImport(dataSource, sql, mapping, impCount, errMsg);
|
||||
}
|
||||
|
||||
logger.info("数据全量导入完成,一共导入 {} 条数据, 耗时: {}", impCount.get(), System.currentTimeMillis() - start);
|
||||
etlResult.setResultMessage("导入ES索引 " + esIndex + " 数据:" + impCount.get() + " 条");
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
errMsg.add(esIndex + " etl failed! ==>" + e.getMessage());
|
||||
}
|
||||
if (errMsg.isEmpty()) {
|
||||
etlResult.setSucceeded(true);
|
||||
} else {
|
||||
etlResult.setErrorMessage(Joiner.on("\n").join(errMsg));
|
||||
}
|
||||
return etlResult;
|
||||
logger.info("start etl to import data to index: {}", mapping.get_index());
|
||||
String sql = mapping.getSql();
|
||||
return importData(sql, params);
|
||||
}
|
||||
|
||||
private void processFailBulkResponse(BulkResponse bulkResponse) {
|
||||
@@ -171,10 +71,12 @@ public class ESEtlService {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean executeSqlImport(DataSource ds, String sql, ESMapping mapping, AtomicLong impCount,
|
||||
List<String> errMsg) {
|
||||
protected boolean executeSqlImport(DataSource ds, String sql, List<Object> values,
|
||||
AdapterConfig.AdapterMapping adapterMapping, AtomicLong impCount,
|
||||
List<String> errMsg) {
|
||||
try {
|
||||
Util.sqlRS(ds, sql, rs -> {
|
||||
ESMapping mapping = (ESMapping) adapterMapping;
|
||||
Util.sqlRS(ds, sql, values, rs -> {
|
||||
int count = 0;
|
||||
try {
|
||||
BulkRequestBuilder bulkRequestBuilder = transportClient.prepareBulk();
|
||||
|
||||
@@ -17,5 +17,5 @@ esMapping:
|
||||
from biz_order t"
|
||||
skips:
|
||||
- customer_id
|
||||
etlCondition: "where t.c_time>='{0}'"
|
||||
etlCondition: "where t.c_time>={}"
|
||||
commitBatch: 3000
|
||||
|
||||
@@ -9,7 +9,7 @@ esMapping:
|
||||
customer_order:
|
||||
name: customer
|
||||
sql: "select t.id, t.name, t.email from customer t"
|
||||
etlCondition: "where t.c_time>='{0}'"
|
||||
etlCondition: "where t.c_time>={}"
|
||||
commitBatch: 3000
|
||||
|
||||
|
||||
|
||||
@@ -12,5 +12,5 @@ esMapping:
|
||||
left join role b on b.id=a.role_id"
|
||||
# objFields:
|
||||
# _labels: array:;
|
||||
etlCondition: "where a.c_time>='{0}'"
|
||||
etlCondition: "where a.c_time>={}"
|
||||
commitBatch: 3000
|
||||
|
||||
Reference in New Issue
Block a user