@@ -81,6 +81,10 @@ public abstract class AbstractSQLConfig implements SQLConfig {
8181 public static String DEFAULT_SCHEMA = "sys" ;
8282 public static String PREFFIX_DISTINCT = "DISTINCT " ;
8383
84+ // * 和 / 不能同时出现,防止 /* */ 段注释! # 和 -- 不能出现,防止行注释! ; 不能出现,防止隔断SQL语句!空格不能出现,防止 CRUD,DROP,SHOW TABLES等语句!
85+ private static final Pattern PATTERN_RANGE ;
86+ private static final Pattern PATTERN_FUNCTION ;
87+
8488 /**
8589 * 表名映射,隐藏真实表名,对安全要求很高的表可以这么做
8690 */
@@ -89,7 +93,11 @@ public abstract class AbstractSQLConfig implements SQLConfig {
8993 public static final List <String > DATABASE_LIST ;
9094 // 自定义原始 SQL 片段 Map<key, substring>:当 substring 为 null 时忽略;当 substring 为 "" 时整个 value 是 raw SQL;其它情况则只是 substring 这段为 raw SQL
9195 public static final Map <String , String > RAW_MAP ;
92- static {
96+ static { // 凡是 SQL 边界符、分隔符、注释符 都不允许,例如 ' " ` ( ) ; # -- ,以免拼接 SQL 时被注入意外可执行指令
97+ PATTERN_RANGE = Pattern .compile ("^[0-9%,!=\\ <\\ >/\\ .\\ +\\ -\\ *\\ ^]+$" ); // ^[a-zA-Z0-9_*%!=<>(),"]+$ 导致 exists(select*from(Comment)) 通过!
98+ PATTERN_FUNCTION = Pattern .compile ("^[A-Za-z0-9%,:_@&~!=\\ <\\ >\\ |\\ [\\ ]\\ {\\ } /\\ .\\ +\\ -\\ *\\ ^\\ ?\\ $]+$" ); //TODO 改成更好的正则,校验前面为单词,中间为操作符,后面为值
99+
100+
93101 TABLE_KEY_MAP = new HashMap <String , String >();
94102 TABLE_KEY_MAP .put (Table .class .getSimpleName (), Table .TABLE_NAME );
95103 TABLE_KEY_MAP .put (Column .class .getSimpleName (), Column .TABLE_NAME );
@@ -119,6 +127,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
119127 RAW_MAP = new LinkedHashMap <>(); // 保证顺序,避免配置冲突等意外情况
120128 }
121129
130+
122131 @ Override
123132 public boolean limitSQLCount () {
124133 return Log .DEBUG == false || AbstractVerifier .SYSTEM_ACCESS_MAP .containsKey (getTable ()) == false ;
@@ -487,7 +496,7 @@ public AbstractSQLConfig setHaving(String having) {
487496 this .having = having ;
488497 return this ;
489498 }
490- /**
499+ /**TODO @having 改为默认 | 或连接,且支持 @having: { "key1>": 1, "key{}": "length(key2)>0", "@combine": "key1,key2" }
491500 * @return HAVING conditoin0 AND condition1 OR condition2 ...
492501 */
493502 @ JSONField (serialize = false )
@@ -517,12 +526,17 @@ public String getHavingString(boolean hasPrefix) {
517526 }
518527 }
519528
520- having = StringUtil .getTrimedString (having );
521- String [] keys = StringUtil .split (having , ";" );
529+ String [] keys = StringUtil .split (getHaving (), ";" );
522530 if (keys == null || keys .length <= 0 ) {
523531 return StringUtil .isEmpty (joinHaving , true ) ? "" : (hasPrefix ? " HAVING " : "" ) + joinHaving ;
524532 }
533+
534+ String quote = getQuote ();
535+ String tableAlias = getAliasWithQuote ();
525536
537+ List <String > raw = getRaw ();
538+ boolean containRaw = raw != null && raw .contains (KEY_HAVING );
539+
526540 String expression ;
527541 String method ;
528542 //暂时不允许 String prefix;
@@ -533,13 +547,31 @@ public String getHavingString(boolean hasPrefix) {
533547
534548 //fun(arg0,arg1,...)
535549 expression = keys [i ];
550+ if (containRaw ) {
551+ try {
552+ String rawSQL = getRawSQL (KEY_HAVING , expression );
553+ if (rawSQL != null ) {
554+ keys [i ] = rawSQL ;
555+ continue ;
556+ }
557+ } catch (Exception e ) {
558+ Log .e (TAG , "newSQLConfig rawColumnSQL == null >> try { "
559+ + " String rawSQL = ((AbstractSQLConfig) config).getRawSQL(KEY_COLUMN, fk); ... "
560+ + "} catch (Exception e) = " + e .getMessage ());
561+ }
562+ }
563+
564+ if (expression .length () > 50 ) {
565+ throw new UnsupportedOperationException ("@having:value 的 value 中字符串 " + expression + " 不合法!"
566+ + "不允许传超过 50 个字符的函数或表达式!请用 @raw 简化传参!" );
567+ }
536568
537569 int start = expression .indexOf ("(" );
538570 if (start < 0 ) {
539- if (isPrepared () && PATTERN_HAVING .matcher (expression ).matches () == false ) {
571+ if (isPrepared () && PATTERN_FUNCTION .matcher (expression ).matches () == false ) {
540572 throw new UnsupportedOperationException ("字符串 " + expression + " 不合法!"
541573 + "预编译模式下 @having:\" column?value;function(arg0,arg1,...)?value...\" "
542- + " 中 column?value 必须符合正则表达式 ^[A-Za-z0-9%!=<>]+$ !不允许空格!" );
574+ + " 中 column?value 必须符合正则表达式 " + PATTERN_FUNCTION + " 且不包含连续减号 -- !不允许空格!" );
543575 }
544576 continue ;
545577 }
@@ -560,24 +592,40 @@ public String getHavingString(boolean hasPrefix) {
560592
561593 suffix = expression .substring (end + 1 , expression .length ());
562594
563- if (isPrepared () && PATTERN_HAVING_SUFFIX . matcher ((String ) suffix ).matches () == false ) {
595+ if (isPrepared () && ((( String ) suffix ). contains ( "--" ) || PATTERN_RANGE . matcher ((String ) suffix ).matches () == false ) ) {
564596 throw new UnsupportedOperationException ("字符串 " + suffix + " 不合法!"
565597 + "预编译模式下 @having:\" column?value;function(arg0,arg1,...)?value...\" "
566- + " 中 ?value 必须符合正则表达式 ^[0-9%!=<>]+$ !不允许空格 !" );
598+ + " 中 ?value 必须符合正则表达式 " + PATTERN_RANGE + " 且不包含连续减号 -- !不允许多余的空格 !" );
567599 }
568600
569601 String [] ckeys = StringUtil .split (expression .substring (start + 1 , end ));
570602
571603 if (ckeys != null ) {
572604 for (int j = 0 ; j < ckeys .length ; j ++) {
605+ String origin = ckeys [j ];
606+
607+ if (isPrepared ()) {
608+ if (origin .startsWith ("_" ) || origin .contains ("--" ) || PATTERN_FUNCTION .matcher (origin ).matches () == false ) {
609+ throw new IllegalArgumentException ("字符 " + ckeys [j ] + " 不合法!"
610+ + "预编译模式下 @having:\" column?value;function(arg0,arg1,...)?value...\" "
611+ + " 中所有 column, arg 都必须是1个不以 _ 开头的单词 或者 符合正则表达式 " + PATTERN_FUNCTION + " 且不包含连续减号 -- !不允许多余的空格!" );
612+ }
613+ }
573614
574- if (isPrepared () && (StringUtil .isName (ckeys [j ]) == false || ckeys [j ].startsWith ("_" ))) {
575- throw new IllegalArgumentException ("字符 " + ckeys [j ] + " 不合法!"
576- + "预编译模式下 @having:\" column?value;function(arg0,arg1,...)?value...\" "
577- + " 中所有 arg 都必须是1个不以 _ 开头的单词!并且不要有空格!" );
615+ //JOIN 副表不再在外层加副表名前缀 userId AS `Commet.userId`, 而是直接 userId AS `userId`
616+ boolean isName = false ;
617+ if (StringUtil .isNumer (origin )) {
618+ //do nothing
619+ }
620+ else if (StringUtil .isName (origin )) {
621+ origin = quote + origin + quote ;
622+ isName = true ;
623+ }
624+ else {
625+ origin = getValue (origin ).toString ();
578626 }
579627
580- ckeys [j ] = getKey ( ckeys [ j ]) ;
628+ ckeys [j ] = ( isName && isKeyPrefix () ? tableAlias + "." : "" ) + origin ;
581629 }
582630 }
583631
@@ -876,8 +924,8 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
876924 //fun(arg0,arg1,...)
877925 expression = keys [i ];
878926
879- if (containRaw ) {
880- if (RAW_MAP .containsValue (expression ) || "" . equals ( RAW_MAP .get (expression ) )) { // newSQLConfig 提前处理好的
927+ if (containRaw ) { // 由于 HashMap 对 key 做了 hash 处理,所以 get 比 containsValue 更快
928+ if ("" . equals ( RAW_MAP .get (expression )) || RAW_MAP .containsValue (expression )) { // newSQLConfig 提前处理好的
881929 continue ;
882930 }
883931
@@ -892,6 +940,11 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
892940 // }
893941 }
894942
943+ if (expression .length () > 50 ) {
944+ throw new UnsupportedOperationException ("@column:value 的 value 中字符串 " + expression + " 不合法!"
945+ + "不允许传超过 50 个字符的函数或表达式!请用 @raw 简化传参!" );
946+ }
947+
895948
896949 int start = expression .indexOf ("(" );
897950 int end = 0 ;
@@ -944,10 +997,10 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
944997 }
945998 else {
946999 // if ((StringUtil.isName(origin) == false || origin.startsWith("_"))) {
947- if (origin .startsWith ("_" ) || PATTERN_FUNCTION .matcher (origin ).matches () == false ) {
1000+ if (origin .startsWith ("_" ) || origin . contains ( "--" ) || PATTERN_FUNCTION .matcher (origin ).matches () == false ) {
9481001 throw new IllegalArgumentException ("字符 " + ckeys [j ] + " 不合法!"
9491002 + "预编译模式下 @column:\" column0,column1:alias;function0(arg0,arg1,...);function1(...):alias...\" "
950- + " 中所有 arg 都必须是1个不以 _ 开头的单词 或者符合正则表达式 " + PATTERN_FUNCTION + " !DISTINCT 必须全大写,且后面必须有且只有 1 个空格!其它情况不允许空格!" );
1003+ + " 中所有 arg 都必须是1个不以 _ 开头的单词 或者符合正则表达式 " + PATTERN_FUNCTION + " 且不包含连续减号 -- !DISTINCT 必须全大写,且后面必须有且只有 1 个空格!其它情况不允许空格!" );
9511004 }
9521005 }
9531006 }
@@ -1626,16 +1679,16 @@ else if (key.endsWith("<")) {
16261679
16271680 switch (keyType ) {
16281681 case 1 :
1629- return getSearchString (key , value );
1682+ return getSearchString (key , value , rawSQL );
16301683 case -2 :
16311684 case 2 :
1632- return getRegExpString (key , value , keyType < 0 );
1685+ return getRegExpString (key , value , keyType < 0 , rawSQL );
16331686 case 3 :
16341687 return getBetweenString (key , value , rawSQL );
16351688 case 4 :
16361689 return getRangeString (key , value , rawSQL );
16371690 case 5 :
1638- return getExistsString (key , value );
1691+ return getExistsString (key , value , rawSQL );
16391692 case 6 :
16401693 return getContainString (key , value , rawSQL );
16411694 case 7 :
@@ -1647,13 +1700,13 @@ else if (key.endsWith("<")) {
16471700 case 10 :
16481701 return getCompareString (key , value , "<" , rawSQL );
16491702 default : // TODO MySQL JSON类型的字段对比 key='[]' 会无结果! key LIKE '[1, 2, 3]' //TODO MySQL , 后面有空格!
1650- return getEqualString (key , value );
1703+ return getEqualString (key , value , rawSQL );
16511704 }
16521705 }
16531706
16541707
16551708 @ JSONField (serialize = false )
1656- public String getEqualString (String key , Object value ) throws Exception {
1709+ public String getEqualString (String key , Object value , String rawSQL ) throws Exception {
16571710 if (JSON .isBooleanOrNumberOrString (value ) == false && value instanceof Subquery == false ) {
16581711 throw new IllegalArgumentException (key + ":value 中value不合法!非PUT请求只支持 [Boolean, Number, String] 内的类型 !" );
16591712 }
@@ -1666,7 +1719,7 @@ public String getEqualString(String key, Object value) throws Exception {
16661719 throw new IllegalArgumentException (key + ":value 中key不合法!不支持 ! 以外的逻辑符 !" );
16671720 }
16681721
1669- return getKey (key ) + (not ? " != " : " = " ) + (value instanceof Subquery ? getSubqueryString ((Subquery ) value ) : getValue (value ));
1722+ return getKey (key ) + (not ? " != " : " = " ) + (value instanceof Subquery ? getSubqueryString ((Subquery ) value ) : ( rawSQL != null ? rawSQL : getValue (value ) ));
16701723 }
16711724
16721725 @ JSONField (serialize = false )
@@ -1729,7 +1782,10 @@ public AbstractSQLConfig setPreparedValueList(List<Object> preparedValueList) {
17291782 * @throws IllegalArgumentException
17301783 */
17311784 @ JSONField (serialize = false )
1732- public String getSearchString (String key , Object value ) throws IllegalArgumentException {
1785+ public String getSearchString (String key , Object value , String rawSQL ) throws IllegalArgumentException {
1786+ if (rawSQL != null ) {
1787+ throw new UnsupportedOperationException ("@raw:value 中 " + key + " 不合法!@raw 不支持 key$ 这种功能符 !只支持 key, key!, key<, key{} 等比较运算 和 @column, @having !" );
1788+ }
17331789 if (value == null ) {
17341790 return "" ;
17351791 }
@@ -1789,7 +1845,10 @@ public String getLikeString(String key, Object value) {
17891845 * @throws IllegalArgumentException
17901846 */
17911847 @ JSONField (serialize = false )
1792- public String getRegExpString (String key , Object value , boolean ignoreCase ) throws IllegalArgumentException {
1848+ public String getRegExpString (String key , Object value , boolean ignoreCase , String rawSQL ) throws IllegalArgumentException {
1849+ if (rawSQL != null ) {
1850+ throw new UnsupportedOperationException ("@raw:value 中 " + key + " 不合法!@raw 不支持 key~ 这种功能符 !只支持 key, key!, key<, key{} 等比较运算 和 @column, @having !" );
1851+ }
17931852 if (value == null ) {
17941853 return "" ;
17951854 }
@@ -1925,18 +1984,6 @@ public String getBetweenString(String key, Object start, Object end) throws Ille
19251984
19261985 //{} range <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
19271986
1928- // * 和 / 不能同时出现,防止 /* */ 段注释! # 和 -- 不能出现,防止行注释! ; 不能出现,防止隔断SQL语句!空格不能出现,防止 CRUD,DROP,SHOW TABLES等语句!
1929- private static final Pattern PATTERN_RANGE ;
1930- private static final Pattern PATTERN_FUNCTION ;
1931- private static final Pattern PATTERN_HAVING ;
1932- private static final Pattern PATTERN_HAVING_SUFFIX ;
1933- static {
1934- PATTERN_RANGE = Pattern .compile ("^[0-9%!=<>,]+$" ); // ^[a-zA-Z0-9_*%!=<>(),"]+$ 导致 exists(select*from(Comment)) 通过!
1935- PATTERN_FUNCTION = Pattern .compile ("^[A-Za-z0-9%-_:!=<> ]+$" ); //TODO 改成更好的正则,校验前面为单词,中间为操作符,后面为值
1936- PATTERN_HAVING = Pattern .compile ("^[A-Za-z0-9%!=<>]+$" ); //TODO 改成更好的正则,校验前面为单词,中间为操作符,后面为值
1937- PATTERN_HAVING_SUFFIX = Pattern .compile ("^[0-9%!=<>]+$" ); // ^[a-zA-Z0-9_*%!=<>(),"]+$ 导致 exists(select*from(Comment)) 通过!
1938- }
1939-
19401987
19411988 /**WHERE key > 'key0' AND key <= 'key1' AND ...
19421989 * @param key
@@ -2070,7 +2117,10 @@ public String getInString(String key, Object[] in, boolean not) throws NotExistE
20702117 * @throws NotExistException
20712118 */
20722119 @ JSONField (serialize = false )
2073- public String getExistsString (String key , Object value ) throws Exception {
2120+ public String getExistsString (String key , Object value , String rawSQL ) throws Exception {
2121+ if (rawSQL != null ) {
2122+ throw new UnsupportedOperationException ("@raw:value 中 " + key + " 不合法!@raw 不支持 key}{ 这种功能符 !只支持 key, key!, key<, key{} 等比较运算 和 @column, @having !" );
2123+ }
20742124 if (value == null ) {
20752125 return "" ;
20762126 }
@@ -2562,6 +2612,22 @@ public static SQLConfig newSQLConfig(RequestMethod method, String table, String
25622612 }
25632613 }
25642614
2615+ if (idIn instanceof List ) { // 排除掉 0, 负数, 空字符串 等无效 id 值
2616+ List <?> ids = ((List <?>) idIn );
2617+ List <Object > newIdIn = new ArrayList <>();
2618+ Object d ;
2619+ for (int i = 0 ; i < ids .size (); i ++) { //不用 idIn.contains(id) 因为 idIn 里存到很可能是 Integer,id 又是 Long!
2620+ d = ids .get (i );
2621+ if ((d instanceof Number && ((Number ) d ).longValue () > 0 ) || (d instanceof String && StringUtil .isNotEmpty (d , true ))) {
2622+ newIdIn .add (d );
2623+ }
2624+ }
2625+ if (newIdIn .isEmpty ()) {
2626+ throw new NotExistException (TAG + ": newSQLConfig idIn instanceof List >> 去掉无效 id 后 newIdIn.isEmpty()" );
2627+ }
2628+ idIn = newIdIn ;
2629+ }
2630+
25652631 //对id和id{}处理,这两个一定会作为条件
25662632 Object id = request .get (idKey );
25672633 if (id != null ) { //null无效
@@ -2786,11 +2852,11 @@ else if (whereList != null && whereList.contains(key)) {
27862852
27872853 List <String > cs = new ArrayList <>();
27882854
2789- String rawColumnSQL = null ;
27902855 List <String > rawList = config .getRaw ();
2791- boolean containRaw = rawList != null && rawList .contains (KEY_COLUMN );
2856+ boolean containColumnRaw = rawList != null && rawList .contains (KEY_COLUMN );
27922857
2793- if (containRaw ) {
2858+ String rawColumnSQL = null ;
2859+ if (containColumnRaw ) {
27942860 try {
27952861 rawColumnSQL = config .getRawSQL (KEY_COLUMN , column );
27962862 if (rawColumnSQL != null ) {
@@ -2809,7 +2875,7 @@ else if (whereList != null && whereList.contains(key)) {
28092875 if (fks != null ) {
28102876 String [] ks ;
28112877 for (String fk : fks ) {
2812- if (containRaw ) {
2878+ if (containColumnRaw ) {
28132879 try {
28142880 String rawSQL = config .getRawSQL (KEY_COLUMN , fk );
28152881 if (rawSQL != null ) {
0 commit comments