diff --git a/src/main/java/com/alibaba/druid/stat/service/DruidStatService.java b/src/main/java/com/alibaba/druid/stat/service/DruidStatService.java new file mode 100644 index 0000000000..434eff9789 --- /dev/null +++ b/src/main/java/com/alibaba/druid/stat/service/DruidStatService.java @@ -0,0 +1,81 @@ +package com.alibaba.druid.stat.service; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.stat.DruidDataSourceStatManager; +import com.alibaba.druid.stat.JdbcSqlStat; +import com.alibaba.druid.stat.service.dto.DataSourceInfo; +import com.alibaba.druid.stat.service.dto.SqlInfo; +import com.alibaba.druid.stat.service.impl.DruidStatMemoryStore; + +public class DruidStatService { + + private ScheduledExecutorService scheduler; + + private ScheduledFuture collectPlan; + + private int collectPeriodSeconds = 60 * 5; + + private DruidStatStore store; + + public synchronized void start() { + if (store != null) { + store = new DruidStatMemoryStore(); + } + + scheduler = Executors.newSingleThreadScheduledExecutor(); + collectPlan = scheduler.scheduleAtFixedRate(new CollectTask(), collectPeriodSeconds, collectPeriodSeconds, + TimeUnit.SECONDS); + } + + public synchronized void stop() { + if (collectPlan == null) { + collectPlan.cancel(true); + } + if (scheduler != null) { + scheduler.shutdown(); + } + } + + public void collect() { + final long timeMillis = System.currentTimeMillis(); + + List dataSourceStatList = new ArrayList(); + for (DruidDataSource dataSource : DruidDataSourceStatManager.getDruidDataSourceInstances()) { + DataSourceInfo dataSourceStat = new DataSourceInfo(); + + dataSourceStat.setUrl(dataSource.getUrl()); + dataSourceStat.setDbType(dataSource.getDbType()); + + dataSourceStatList.add(dataSourceStat); + + Collection sqlStats = dataSource.getDataSourceStat().getSqlStatMap().values(); + + List sqlStatInfoList = new ArrayList(sqlStats.size()); + for (JdbcSqlStat sqlStat : dataSource.getDataSourceStat().getSqlStatMap().values()) { + SqlInfo sqlStatInfo = new SqlInfo(); + + sqlStatInfo.setSql(sqlStat.getSql()); + + sqlStatInfoList.add(sqlStatInfo); + } + } + + store.saveDataSource(timeMillis, dataSourceStatList); + } + + private final class CollectTask implements Runnable { + + public void run() { + collect(); + } + } + +} diff --git a/src/main/java/com/alibaba/druid/stat/service/DruidStatStore.java b/src/main/java/com/alibaba/druid/stat/service/DruidStatStore.java new file mode 100644 index 0000000000..da02e2e1fd --- /dev/null +++ b/src/main/java/com/alibaba/druid/stat/service/DruidStatStore.java @@ -0,0 +1,13 @@ +package com.alibaba.druid.stat.service; + +import java.util.List; + +import com.alibaba.druid.stat.service.dto.DataSourceInfo; +import com.alibaba.druid.stat.service.dto.SqlInfo; + +public interface DruidStatStore { + + void saveSql(long timeMillis, List sqlList); + + void saveDataSource(long timeMillis, List dataSourceList); +} diff --git a/src/main/java/com/alibaba/druid/stat/service/dto/DataSourceInfo.java b/src/main/java/com/alibaba/druid/stat/service/dto/DataSourceInfo.java new file mode 100644 index 0000000000..2b27dadf97 --- /dev/null +++ b/src/main/java/com/alibaba/druid/stat/service/dto/DataSourceInfo.java @@ -0,0 +1,36 @@ +package com.alibaba.druid.stat.service.dto; + +import java.util.List; + +public class DataSourceInfo { + + private String url; + private String dbType; + + private List sqlList; + + public String getDbType() { + return dbType; + } + + public void setDbType(String dbType) { + this.dbType = dbType; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public List getSqlList() { + return sqlList; + } + + public void setSqlList(List sqlList) { + this.sqlList = sqlList; + } + +} diff --git a/src/main/java/com/alibaba/druid/stat/service/dto/SqlInfo.java b/src/main/java/com/alibaba/druid/stat/service/dto/SqlInfo.java new file mode 100644 index 0000000000..4723830571 --- /dev/null +++ b/src/main/java/com/alibaba/druid/stat/service/dto/SqlInfo.java @@ -0,0 +1,83 @@ +package com.alibaba.druid.stat.service.dto; + +public class SqlInfo { + + private String sql; + private int executeCount; + private int runningCount; + private int concurrentMax; + private int executeErrorCount; + private int inTransactionCount; + + private long fetchRowCount; + private long updateCount; + + public SqlInfo(){ + + } + + public String getSql() { + return sql; + } + + public void setSql(String sql) { + this.sql = sql; + } + + public int getExecuteCount() { + return executeCount; + } + + public void setExecuteCount(int executeCount) { + this.executeCount = executeCount; + } + + public int getRunningCount() { + return runningCount; + } + + public void setRunningCount(int runningCount) { + this.runningCount = runningCount; + } + + public int getConcurrentMax() { + return concurrentMax; + } + + public void setConcurrentMax(int concurrentMax) { + this.concurrentMax = concurrentMax; + } + + public int getExecuteErrorCount() { + return executeErrorCount; + } + + public void setExecuteErrorCount(int executeErrorCount) { + this.executeErrorCount = executeErrorCount; + } + + public int getInTransactionCount() { + return inTransactionCount; + } + + public void setInTransactionCount(int inTransactionCount) { + this.inTransactionCount = inTransactionCount; + } + + public long getFetchRowCount() { + return fetchRowCount; + } + + public void setFetchRowCount(long fetchRowCount) { + this.fetchRowCount = fetchRowCount; + } + + public long getUpdateCount() { + return updateCount; + } + + public void setUpdateCount(long updateCount) { + this.updateCount = updateCount; + } + +} diff --git a/src/main/java/com/alibaba/druid/stat/service/impl/DruidStatMemoryStore.java b/src/main/java/com/alibaba/druid/stat/service/impl/DruidStatMemoryStore.java new file mode 100644 index 0000000000..674005e394 --- /dev/null +++ b/src/main/java/com/alibaba/druid/stat/service/impl/DruidStatMemoryStore.java @@ -0,0 +1,22 @@ +package com.alibaba.druid.stat.service.impl; + +import java.util.List; + +import com.alibaba.druid.stat.service.DruidStatStore; +import com.alibaba.druid.stat.service.dto.DataSourceInfo; +import com.alibaba.druid.stat.service.dto.SqlInfo; + + +public class DruidStatMemoryStore implements DruidStatStore { + + @Override + public void saveSql(long timeMillis, List sqlList) { + + } + + @Override + public void saveDataSource(long timeMillis, List dataSourceList) { + + } + +} diff --git a/src/main/java/com/alibaba/druid/wall/WallConfig.java b/src/main/java/com/alibaba/druid/wall/WallConfig.java index 045cfd31fb..655e4387c3 100644 --- a/src/main/java/com/alibaba/druid/wall/WallConfig.java +++ b/src/main/java/com/alibaba/druid/wall/WallConfig.java @@ -11,6 +11,7 @@ public class WallConfig implements WallConfigMBean { private boolean noneBaseStatementAllow = false; + private boolean callAllow = true; private boolean selelctAllow = true; private boolean selectIntoAllow = true; private boolean selectIntoOutfileAllow = false; @@ -319,4 +320,13 @@ public class WallConfig implements WallConfigMBean { name = WallVisitorUtils.form(name); return this.permitFunctions.contains(name); } + + public boolean isCallAllow() { + return callAllow; + } + + public void setCallAllow(boolean callAllow) { + this.callAllow = callAllow; + } + } diff --git a/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java b/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java index d620099e50..ceb97b2395 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java +++ b/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java @@ -13,6 +13,7 @@ import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLNumericLiteralExpr; import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; +import com.alibaba.druid.sql.ast.statement.SQLCallStatement; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; @@ -232,6 +233,8 @@ public class MySqlWallVisitor extends MySqlASTVisitorAdapter implements WallVisi } else if (x instanceof SQLDeleteStatement) { } else if (x instanceof SQLUpdateStatement) { + + } else if (x instanceof SQLCallStatement) { } else if (x instanceof SQLTruncateStatement) { if (!config.isTruncateAllow()) { diff --git a/src/main/java/com/alibaba/druid/wall/spi/OracleWallVisitor.java b/src/main/java/com/alibaba/druid/wall/spi/OracleWallVisitor.java index db44717582..1cd33a6c54 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/OracleWallVisitor.java +++ b/src/main/java/com/alibaba/druid/wall/spi/OracleWallVisitor.java @@ -12,6 +12,7 @@ import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLInListExpr; import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; +import com.alibaba.druid.sql.ast.statement.SQLCallStatement; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; @@ -167,6 +168,7 @@ public class OracleWallVisitor extends OracleASTVisitorAdapter implements WallVi } else if (x instanceof SQLUpdateStatement) { } else if (x instanceof OracleMultiInsertStatement) { } else if (x instanceof OracleMergeStatement) { + } else if (x instanceof SQLCallStatement) { } else if (x instanceof SQLTruncateStatement) { if (!config.isTruncateAllow()) { violations.add(new IllegalSQLObjectViolation(toSQL(x))); diff --git a/src/main/java/com/alibaba/druid/wall/spi/SQLServerWallVisitor.java b/src/main/java/com/alibaba/druid/wall/spi/SQLServerWallVisitor.java index 8795bd183a..f56cd691dc 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/SQLServerWallVisitor.java +++ b/src/main/java/com/alibaba/druid/wall/spi/SQLServerWallVisitor.java @@ -13,6 +13,7 @@ import com.alibaba.druid.sql.ast.expr.SQLInListExpr; import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; +import com.alibaba.druid.sql.ast.statement.SQLCallStatement; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; @@ -147,6 +148,8 @@ public class SQLServerWallVisitor extends SQLServerASTVisitorAdapter implements } else if (x instanceof SQLDeleteStatement) { } else if (x instanceof SQLUpdateStatement) { + } else if (x instanceof SQLCallStatement) { + } else if (x instanceof SQLTruncateStatement) { if (!config.isTruncateAllow()) { violations.add(new IllegalSQLObjectViolation(toSQL(x))); diff --git a/src/test/java/com/alibaba/druid/bvt/filter/wall/WallCallTest.java b/src/test/java/com/alibaba/druid/bvt/filter/wall/WallCallTest.java new file mode 100644 index 0000000000..5ef27242ab --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/filter/wall/WallCallTest.java @@ -0,0 +1,27 @@ +package com.alibaba.druid.bvt.filter.wall; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.wall.WallUtils; + +/** + * 这个场景,被攻击者用于测试当前SQL拥有多少字段 + * @author wenshao + * + */ +public class WallCallTest extends TestCase { + + public void testMySql() throws Exception { + Assert.assertTrue(WallUtils.isValidateMySql("{ call INTERFACE_DATA_EXTRACTION.INVOICE_INFO(?,?,?)}")); + } + + public void testOracle() throws Exception { + Assert.assertTrue(WallUtils.isValidateOracle("{ call INTERFACE_DATA_EXTRACTION.INVOICE_INFO(?,?,?)}")); + } + + public void testSqlServer() throws Exception { + Assert.assertTrue(WallUtils.isValidateSqlServer("{ call INTERFACE_DATA_EXTRACTION.INVOICE_INFO(?,?,?)}")); + } +}