diff --git a/README.md b/README.md index d5300c3..3ecc3eb 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,47 @@ 详细文档,计划2019年1月1日左右更新 +## 未来 + +1. 现在的程序是写死的,处理逻辑能以链的形式组合就好了 + +@pipe("name") + +如果能够自动装配的话,最好有一个 + +2. 使用Mybatis别名的技巧来实现自动装配 + +```json +{ + "T":{ + "@column":"a,b,c" + }, + "T2":{ + "@parent":"T" + "@column":"d,e,f" + } +} +``` + +查询结果: + +```json + { + "T":{ + "a":"a", + "b":"b", + "c":"c", + "T2":{ + "d":"d", + "e":"e", + "f":"f" + } + } + } +``` + +3. 表,字段的黑白名单放出来,作为 + +4. 权限的控制通过 + + \ No newline at end of file diff --git a/src/main/java/zuo/biao/apijson/parser/APIJSONProvider.java b/src/main/java/zuo/biao/apijson/parser/APIJSONProvider.java index 6268cd9..18bb3ac 100644 --- a/src/main/java/zuo/biao/apijson/parser/APIJSONProvider.java +++ b/src/main/java/zuo/biao/apijson/parser/APIJSONProvider.java @@ -12,6 +12,11 @@ public class APIJSONProvider extends AbstractProvider { private final String ALIAS_SPLIT = ":"; private static final String AND = " AND "; private static final String OR = " OR "; + + private List tableWhiteList = new ArrayList(); + private List tableBlackList = new ArrayList(); + private List columnWhiteList = new ArrayList(); + private List columnBlackList = new ArrayList(); private JSONObject request; private JSONObject join; @@ -44,6 +49,10 @@ public APIJSONProvider(JSONObject obj) { /** * 解析请求中的表名 * 表名必须符合:(\w+(:\w+)?) + * 即: + * 表名 + * 表名:表别名 + * 两种形式 */ @Override public List getTables() { @@ -65,7 +74,7 @@ public List getTables() { }else { tableRealName = tableAliasName = tableName; } - + validateTable(tableRealName); list.add(tableRealName + " " + tableAliasName); } return list; @@ -87,7 +96,7 @@ public List getTables() { * 支持函数的正则:((\w+\(\w+\):\w+|\w+)(:\w+)?)+(\s?,\s?((\w+\(\w+\):\w+|\w+)(:\w+)?)+)* * 例:a,b,c或a:a1,b:b1,c * 约束:必须要有表名 - * 目前不支持函数 + * 目前支持函数 */ @Override public List getSelect() { @@ -95,6 +104,7 @@ public List getSelect() { List list = new ArrayList<>(); if(getStatementType() == StatementType.SELECT) { Set tableNames = request.keySet(); + String tableRealName; String tableAliasName; for(String tableName : tableNames) { //格式检查 @@ -104,16 +114,18 @@ public List getSelect() { } //是否有自定义别名 if(tableName.contains(ALIAS_SPLIT)) { + tableRealName = tableName.split(ALIAS_SPLIT)[0]; //填写了表别名 tableAliasName = tableName.split(ALIAS_SPLIT)[1]; }else { - tableAliasName = tableName; + tableRealName = tableAliasName = tableName; } // 获取请求@column的值 JSONObject propertis = request.getJSONObject(tableName); String columnsValue = propertis.getString("@column"); if(columnsValue == null) { + validateColumn(tableRealName,"*"); //没有填写@column字段,默认为全部 list.add(tableAliasName + ".*"); }else { @@ -138,15 +150,18 @@ public List getSelect() { //此处省略函数合法性检查的代码... String columnRealName = functionStrs[1]; String columnAliasName = columnName.split(ALIAS_SPLIT)[1]; + validateColumn(tableRealName,columnRealName); list.add(funcitonName + "(" + tableAliasName + "." + columnRealName + ")" + " as " + columnAliasName); }else { //没函数的字段 String columnRealName = functionOrColumn ; String columnAliasName = columnName.split(ALIAS_SPLIT)[1]; + validateColumn(tableRealName,columnRealName); list.add(tableAliasName + "." + columnRealName + " as " + columnAliasName); } }else { //使用tableAliasName.columnName类型 + validateColumn(tableRealName,columnName); list.add(tableAliasName + "." + columnName); } } @@ -359,7 +374,7 @@ public List getWhere() { } /** * 内连接 - * 请求:table1.column1 = table2.column2 + * 请求:"@innerJoin" : ["table1.column1 = table2.column2","table1.column1 = table2.column2"] * 编译之后:INNER JOIN table1 ON table1.column1=table2.column2 */ @Override @@ -368,22 +383,26 @@ public List getInnerJoin() { List list = new ArrayList<>(); if(getStatementType() == StatementType.SELECT) { if(join != null && join.get("@innerJoin")!=null) { - Object obj = join.get("@innerJoin"); - if(obj instanceof String) { - String joinStr = (String)obj; - if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { - //table1.column1 = table2.column2 - String[] tcs = joinStr.replaceAll("\\s", "").split("="); - String leftTable = tcs[0].split("\\.")[0]; - String rightTable = tcs[1].split("\\.")[0]; - list.add(leftTable + " ON " + joinStr); + JSONArray stms = join.getJSONArray("@innerJoin"); + for(int i = 0; i < stms.size(); i++) { + Object obj = stms.get(i); + if(obj instanceof String) { + String joinStr = (String)obj; + if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { + //table1.column1 = table2.column2 + String[] tcs = joinStr.replaceAll("\\s", "").split("="); + String leftTable = tcs[0].split("\\.")[0]; + String rightTable = tcs[1].split("\\.")[0]; + validateTable(leftTable); + list.add(leftTable + " ON " + joinStr); + }else { + error("@innerJoin的格式必须是:table1.column1 = table2.column2,相当于INNER JOIN table1 ON table1.column1=table2.column2"); + return null; + } }else { - error("@innerJoin的格式必须是:table1.column1 = table2.column2,相当于INNER JOIN table1 ON table1.column1=table2.column2"); + error("@innerJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于INNER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); return null; } - }else { - error("@innerJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于INNER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); - return null; } } } @@ -399,22 +418,26 @@ public List getLeftOuterJoin() { List list = new ArrayList<>(); if(getStatementType() == StatementType.SELECT) { if(join != null && join.get("@leftOuterJoin")!=null) { - Object obj = join.get("@leftOuterJoin"); - if(obj instanceof String) { - String joinStr = (String)obj; - if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { - //table1.column1 = table2.column2 - String[] tcs = joinStr.replaceAll("\\s", "").split("="); - String leftTable = tcs[0].split("\\.")[0]; - String rightTable = tcs[1].split("\\.")[0]; - list.add(leftTable + " ON " + joinStr); + JSONArray stms = join.getJSONArray("@leftOuterJoin"); + for(int i = 0; i < stms.size(); i++) { + Object obj = stms.get(i); + if(obj instanceof String) { + String joinStr = (String)obj; + if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { + //table1.column1 = table2.column2 + String[] tcs = joinStr.replaceAll("\\s", "").split("="); + String leftTable = tcs[0].split("\\.")[0]; + String rightTable = tcs[1].split("\\.")[0]; + validateTable(leftTable); + list.add(leftTable + " ON " + joinStr); + }else { + error("@leftOuterJoin的格式必须是:table1.column1 = table2.column2,相当于LEFT OUTER JOIN table1 ON table1.column1=table2.column2"); + return null; + } }else { - error("@leftOuterJoin的格式必须是:table1.column1 = table2.column2,相当于LEFT OUTER JOIN table1 ON table1.column1=table2.column2"); + error("@leftOuterJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于LEFT OUTER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); return null; } - }else { - error("@leftOuterJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于LEFT OUTER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); - return null; } } } @@ -429,22 +452,26 @@ public List getRightOuterJoin() { List list = new ArrayList<>(); if(getStatementType() == StatementType.SELECT) { if(join != null && join.get("@rightOuterJoin")!=null) { - Object obj = join.get("@rightOuterJoin"); - if(obj instanceof String) { - String joinStr = (String)obj; - if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { - //table1.column1 = table2.column2 - String[] tcs = joinStr.replaceAll("\\s", "").split("="); - String leftTable = tcs[0].split("\\.")[0]; - String rightTable = tcs[1].split("\\.")[0]; - list.add(leftTable + " ON " + joinStr); + JSONArray stms = join.getJSONArray("@rightOuterJoin"); + for(int i = 0; i < stms.size(); i++) { + Object obj = stms.get(i); + if(obj instanceof String) { + String joinStr = (String)obj; + if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { + //table1.column1 = table2.column2 + String[] tcs = joinStr.replaceAll("\\s", "").split("="); + String leftTable = tcs[0].split("\\.")[0]; + String rightTable = tcs[1].split("\\.")[0]; + validateTable(leftTable); + list.add(leftTable + " ON " + joinStr); + }else { + error("@rightOuterJoin的格式必须是:table1.column1 = table2.column2,相当于RIGHT OUTER JOIN table1 ON table1.column1=table2.column2"); + return null; + } }else { - error("@rightOuterJoin的格式必须是:table1.column1 = table2.column2,相当于RIGHT OUTER JOIN table1 ON table1.column1=table2.column2"); + error("@rightOuterJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于RIGHT OUTER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); return null; } - }else { - error("@rightOuterJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于RIGHT OUTER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); - return null; } } } @@ -459,22 +486,26 @@ public List getJoin() { List list = new ArrayList<>(); if(getStatementType() == StatementType.SELECT) { if(join != null && join.get("@join")!=null) { - Object obj = join.get("@join"); - if(obj instanceof String) { - String joinStr = (String)obj; - if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { - //table1.column1 = table2.column2 - String[] tcs = joinStr.replaceAll("\\s", "").split("="); - String leftTable = tcs[0].split("\\.")[0]; - String rightTable = tcs[1].split("\\.")[0]; - list.add(leftTable + " ON " + joinStr); + JSONArray stms = join.getJSONArray("@join"); + for(int i = 0; i < stms.size(); i++) { + Object obj = stms.get(i); + if(obj instanceof String) { + String joinStr = (String)obj; + if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { + //table1.column1 = table2.column2 + String[] tcs = joinStr.replaceAll("\\s", "").split("="); + String leftTable = tcs[0].split("\\.")[0]; + String rightTable = tcs[1].split("\\.")[0]; + validateTable(leftTable); + list.add(leftTable + " ON " + joinStr); + }else { + error("@join的格式必须是:table1.column1 = table2.column2,相当于JOIN table1 ON table1.column1=table2.column2"); + return null; + } }else { - error("@join的格式必须是:table1.column1 = table2.column2,相当于JOIN table1 ON table1.column1=table2.column2"); + error("@join的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); return null; } - }else { - error("@join的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); - return null; } } } @@ -489,22 +520,27 @@ public List getOuterJoin() { List list = new ArrayList<>(); if(getStatementType() == StatementType.SELECT) { if(join != null && join.get("@outerJoin")!=null) { - Object obj = join.get("@outerJoin"); - if(obj instanceof String) { - String joinStr = (String)obj; - if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { - //table1.column1 = table2.column2 - String[] tcs = joinStr.replaceAll("\\s", "").split("="); - String leftTable = tcs[0].split("\\.")[0]; - String rightTable = tcs[1].split("\\.")[0]; - list.add(leftTable + " ON " + joinStr); + JSONArray stms = join.getJSONArray("@join"); + for(int i = 0; i < stms.size(); i++) { + + Object obj = stms.get(i); + if(obj instanceof String) { + String joinStr = (String)obj; + if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { + //table1.column1 = table2.column2 + String[] tcs = joinStr.replaceAll("\\s", "").split("="); + String leftTable = tcs[0].split("\\.")[0]; + String rightTable = tcs[1].split("\\.")[0]; + validateTable(leftTable); + list.add(leftTable + " ON " + joinStr); + }else { + error("@outerJoin的格式必须是:table1.column1 = table2.column2,相当于OUTER JOIN table1 ON table1.column1=table2.column2"); + return null; + } }else { - error("@outerJoin的格式必须是:table1.column1 = table2.column2,相当于OUTER JOIN table1 ON table1.column1=table2.column2"); + error("@outerJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于OUTER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); return null; } - }else { - error("@outerJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于OUTER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); - return null; } } } @@ -630,6 +666,7 @@ public List getColumns() { continue; if(condition.matches("\\w+")) { //纯字段名 + validateColumn(tableName,condition); list.add(condition); }else { error("新增时,"+condition+"必须是字段名"); @@ -724,8 +761,10 @@ public List getSets() { propertis.get(condition) instanceof Float || propertis.get(condition) instanceof Double || propertis.get(condition) instanceof BigDecimal) { + validateColumn(tableName,columnName); list.add(tableName + "." + columnName + "=" + propertis.get(condition).toString() ); }else if(propertis.get(condition) instanceof String) { + validateColumn(tableName, columnName); list.add(tableName + "." + columnName + "=" + "'" + ((String) propertis.get(condition)).replaceAll("'", "''") + "'"); } } @@ -735,4 +774,102 @@ public List getSets() { } return list; } + + /* + * ================================== + * 权限逻辑 + * ================================== + * + * 不管权限认证系统有多复杂,最后到生成SQL这步 + * 都是进行黑白名单的检查 + * 不论黑白,只要名单为空,表示所有数据都可以 + * 表名单: + * 格式:表名 + * 大小写不敏感 + * 字段名单: + * 格式:表名.字段 + * 大小写不敏感 + * 所有字段: + * 格式: 表名.* + * + * 如果想要新增或者修改的更复杂的逻辑, + * 请在外层处理完成之后以制定格式提交该SQL的黑白名单即可 + * + */ + /** + * 表的黑白名单检查 + * @param tableName + */ + private void validateTable(String tableName) { + String tableUCN = tableName.toUpperCase(); + if(tableBlackList != null && !tableBlackList.isEmpty()) { + for(String tn : tableBlackList) { + if(tableUCN.equals(tn.toUpperCase())) { + error("请求的表:" + tableName + "在黑名单中"); + return; + } + } + } + + if(tableWhiteList != null && !tableWhiteList.isEmpty()) { + for(String tn : tableWhiteList) { + if(tableUCN.equals(tn.toUpperCase())) { + return; + } + } + error("请求的表:" + tableName + "不在白名单中"); + } + } + /** + * 字段黑白名单检查 + * 要求: "表.列" + * @param columnName + */ + private void validateColumn(String tableName, String columnName) { + String tableUCN = tableName.toUpperCase(); + String columnUCN = columnName.toUpperCase(); + String tcUCN = tableUCN + "." + columnUCN; + if(columnBlackList != null && !columnBlackList.isEmpty()) { + for(String tc : columnBlackList) { + if(tcUCN.equals(tc.toUpperCase())) { + error("请求的字段:" + tcUCN + "在黑名单中"); + return; + } + } + } + + if(columnWhiteList != null && !columnWhiteList.isEmpty()) { + for(String tc : columnWhiteList) { + if(tc.endsWith(".*") || tcUCN.equals(tc.toUpperCase())) { + return; + } + } + error("请求的字段:" + tcUCN + "不在白名单中"); + } + } + + public List getTableWhiteList() { + return tableWhiteList; + } + public void setTableWhiteList(List tableWhiteList) { + this.tableWhiteList = tableWhiteList; + } + public List getTableBlackList() { + return tableBlackList; + } + public void setTableBlackList(List tableBlackList) { + this.tableBlackList = tableBlackList; + } + public List getColumnWhiteList() { + return columnWhiteList; + } + public void setColumnWhiteList(List columnWhiteList) { + this.columnWhiteList = columnWhiteList; + } + public List getColumnBlackList() { + return columnBlackList; + } + public void setColumnBlackList(List columnBlackList) { + this.columnBlackList = columnBlackList; + } } diff --git a/src/main/java/zuo/biao/apijson/parser/App.java b/src/main/java/zuo/biao/apijson/parser/App.java index 2688c5f..d623df2 100644 --- a/src/main/java/zuo/biao/apijson/parser/App.java +++ b/src/main/java/zuo/biao/apijson/parser/App.java @@ -1,5 +1,7 @@ package zuo.biao.apijson.parser; +import java.util.Set; + import com.alibaba.fastjson.JSONObject; /** @@ -12,11 +14,11 @@ public static void main( String[] args ) throws SQLProviderException { long st = System.currentTimeMillis(); String json = "{\r\n" + - " \"Retail\": {\r\n" + - " \"description\": \"20190101,元旦快乐\",\r\n" + - " \"INVOICE_ADDRESS\": 13,\r\n" + - " \"docno\": \"RE1807250000002\"\r\n" + - " }\r\n" + + " \"Retail\":{\r\n" + + " \"id\": 1,\r\n" + + " \"amt\": 1,\r\n" + + " \"@column\": \"id,cc\"\r\n" + + " }\r\n" + "}"; JSONObject req = JSONObject.parseObject(json); long fastJsonet = System.currentTimeMillis(); @@ -24,6 +26,14 @@ public static void main( String[] args ) throws SQLProviderException long st2 = System.currentTimeMillis(); APIJSONProvider apijsonProvider = new APIJSONProvider(req); apijsonProvider.setStatementType(StatementType.SELECT); +// apijsonProvider.getTableBlackList().add("Retail"); +// apijsonProvider.getTableWhiteList().add("Retail"); +// apijsonProvider.getTableWhiteList().add("StorE"); +// apijsonProvider.getColumnBlackList().add("retail.id"); + apijsonProvider.getColumnWhiteList().add("retail.*"); +// apijsonProvider.getColumnWhiteList().add("retail.amt"); +// apijsonProvider.getColumnBlackList().add("retail.amt"); +// apijsonProvider.getColumnWhiteList().add("store.id"); SQLExplorer builder = new SQLExplorer(apijsonProvider); System.out.println(builder.getSQL()); long et = System.currentTimeMillis(); @@ -32,5 +42,6 @@ public static void main( String[] args ) throws SQLProviderException System.out.println("APIJSONParser解析用时:" + (et - st2) + "ms"); System.out.println("合计用时:" + (et - st) + "ms"); //实际解析耗时21ms + } } diff --git a/src/main/java/zuo/biao/apijson/parser/SQLProviderException.java b/src/main/java/zuo/biao/apijson/parser/SQLProviderException.java index 3b0fff7..12c826e 100644 --- a/src/main/java/zuo/biao/apijson/parser/SQLProviderException.java +++ b/src/main/java/zuo/biao/apijson/parser/SQLProviderException.java @@ -1,7 +1,9 @@ package zuo.biao.apijson.parser; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import zuo.biao.apijson.parser.Message.ErrorElement; @@ -9,6 +11,8 @@ public class SQLProviderException extends Exception { private List errors; + private Set errorMsgs = new HashSet<>(); + public SQLProviderException() { } @@ -22,6 +26,7 @@ public SQLProviderException(List errors) { List stackTrace = new ArrayList<>(); StringBuffer sb = new StringBuffer(); for(int i = 0; i < errors.size(); i++) { + errorMsgs.add(errors.get(i).getErrorMsg() + Utils.NEW_LINE); sb.append( i + ".Error message: ----> " + errors.get(i).getErrorMsg() + Utils.NEW_LINE); stackTrace.add(errors.get(i).getStack()); } @@ -30,4 +35,19 @@ public SQLProviderException(List errors) { super.setStackTrace(stackTrace.toArray(stack)); this.errors = errors; } + + public List getErrors() { + return errors; + } + public void setErrors(List errors) { + this.errors = errors; + } + public Set getErrorMsgs() { + return errorMsgs; + } + public void setErrorMsgs(Set errorMsgs) { + this.errorMsgs = errorMsgs; + } + + } diff --git a/target/classes/META-INF/maven/APIJSONParser/APIJSONParser/pom.properties b/target/classes/META-INF/maven/APIJSONParser/APIJSONParser/pom.properties index 2f5b31f..1f4c4e8 100644 --- a/target/classes/META-INF/maven/APIJSONParser/APIJSONParser/pom.properties +++ b/target/classes/META-INF/maven/APIJSONParser/APIJSONParser/pom.properties @@ -1,5 +1,5 @@ #Generated by Maven Integration for Eclipse -#Tue Dec 18 10:59:37 CST 2018 +#Mon Dec 24 14:28:09 CST 2018 version=0.0.1-SNAPSHOT groupId=APIJSONParser m2e.projectName=APIJSONParser diff --git a/target/classes/zuo/biao/apijson/parser/APIJSONProvider.class b/target/classes/zuo/biao/apijson/parser/APIJSONProvider.class index 37f6639..6af2e6f 100644 Binary files a/target/classes/zuo/biao/apijson/parser/APIJSONProvider.class and b/target/classes/zuo/biao/apijson/parser/APIJSONProvider.class differ diff --git a/target/classes/zuo/biao/apijson/parser/App.class b/target/classes/zuo/biao/apijson/parser/App.class index 0b1fdde..9c7cd27 100644 Binary files a/target/classes/zuo/biao/apijson/parser/App.class and b/target/classes/zuo/biao/apijson/parser/App.class differ diff --git a/target/classes/zuo/biao/apijson/parser/SQLProviderException.class b/target/classes/zuo/biao/apijson/parser/SQLProviderException.class index 1054a08..1b17e7b 100644 Binary files a/target/classes/zuo/biao/apijson/parser/SQLProviderException.class and b/target/classes/zuo/biao/apijson/parser/SQLProviderException.class differ