若依(ruoyi)创建表的请求源码分析
请求路径 /tool/gen/createTable
该请求是只能出现创建表的sql语句,所以不要出现drop table if exists table;等语句,这样是会报错的。(之前被这个问题坑过)
该请求处理代码如下,主要分为3部分:
@PostMapping("/createTable")
@ResponseBody
public AjaxResult create(String sql)
{
try
{
//1.判断是否为创建表的语句
SqlUtil.filterKeyword(sql);
//2.进行创建表
List<SQLStatement> sqlStatements = SQLUtils.parseStatements(sql, DbType.mysql);
List<String> tableNames = new ArrayList<>();
for (SQLStatement sqlStatement : sqlStatements)
{
if (sqlStatement instanceof MySqlCreateTableStatement)
{
MySqlCreateTableStatement createTableStatement = (MySqlCreateTableStatement) sqlStatement;
if (genTableService.createTable(createTableStatement.toString()))
{
String tableName = createTableStatement.getTableName().replaceAll("`", "");
tableNames.add(tableName);
}
}
}
//3.在gen_table和gen_table_column表中记录关于创建的表的信息及字段信息
List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames.toArray(new String[tableNames.size()]));
String operName = Convert.toStr(PermissionUtils.getPrincipalProperty("loginName"));
genTableService.importGenTable(tableList, operName);
return AjaxResult.success();
}
catch (Exception e)
{
logger.error(e.getMessage(), e);
return AjaxResult.error("创建表结构异常[" + e.getMessage() + "]");
}
}
接下来一步步分析
SqlUtil.filterKeyword(sql);
这个语句就是用来分析是否为创建表的语句,前面报错的原因就是因为它
它里面的主要内容是这样的
public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";
public static void filterKeyword(String value)
{
if (StringUtils.isEmpty(value))
{
return;
}
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
for (String sqlKeyword : sqlKeywords)
{
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1)
{
throw new UtilException("参数存在SQL注入风险");
}
}
}
其实就是很简单的将所有sql关键字写入SQL_REGEX变量中,然后按照“|”字符切割出这些关键字。在判断sql语句里面是否有这些关键字,若有则报错
List<SQLStatement> sqlStatements = SQLUtils.parseStatements(sql, DbType.mysql);
...
for (SQLStatement sqlStatement : sqlStatements)
{
if (sqlStatement instanceof MySqlCreateTableStatement)
{
MySqlCreateTableStatement createTableStatement = (MySqlCreateTableStatement) sqlStatement;
if (genTableService.createTable(createTableStatement.toString()))
{
String tableName = createTableStatement.getTableName().replaceAll("`", "");
tableNames.add(tableName);
}
}
}
这里就是用阿里的Druid包,进行处理,判断是否为mysql的创建表语句,若是则调用genTableService.createTable进行建表
List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames.toArray(new String[tableNames.size()]));
String operName = Convert.toStr(PermissionUtils.getPrincipalProperty("loginName"));
genTableService.importGenTable(tableList, operName);
这一步很巧妙,它是操作mysql中information_schema数据库中TABLES和COLUMNS表获取出关于所要创建的表的信息。这样就不用在请求的时候携带关于创建时间等请求参数、以及在解析sql得到表名、字段名、字段类型、约束等操作,节省了很多代码。(information_schema数据库是mysql自带的,保存着关于MySQL服务器所维护的所有其他数据库的信息)