doomiwork 4.1.2 → 4.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -129,7 +129,7 @@ module.exports.loadController=(app, routes)=> {
|
|
|
129
129
|
if (modules.length > 0) {
|
|
130
130
|
for (const modulePath of modules) {
|
|
131
131
|
//if (app.logger) app.logger.trace('initlize controller [%s] for baseUrl [%s]', modulePath, baseUrl);
|
|
132
|
-
console.log('initlize controller [%s] for baseUrl [%s]', modulePath, baseUrl);
|
|
132
|
+
// console.log('initlize controller [%s] for baseUrl [%s]', modulePath, baseUrl);
|
|
133
133
|
const controllModule = require(modulePath);
|
|
134
134
|
const controller = new controllModule(app);
|
|
135
135
|
app.use(baseUrl, controller.router);
|
package/core/controller.js
CHANGED
|
@@ -182,11 +182,11 @@ class controller {
|
|
|
182
182
|
*/
|
|
183
183
|
async getListData(req, dataKey, configFile = 0) {
|
|
184
184
|
if (this.logger) this.logger.trace("准备获取dataconfig文件中对应的 %s list数据",dataKey)
|
|
185
|
-
const listinfo = getListInfo(req, dataKey, configFile,this._daoModel);
|
|
185
|
+
const listinfo = getListInfo(req, dataKey, configFile, this._daoModel, ignorefilter==1);
|
|
186
186
|
if (!listinfo) return { successed: false, errcode: -10, errmsg:`缺失${dataKey}对应的查询语句`};
|
|
187
187
|
|
|
188
188
|
////直接操作数据库之前,可由子类再次Handler
|
|
189
|
-
let beforeData = await this.beforeAccessDB(req, listinfo.sql,
|
|
189
|
+
let beforeData = await this.beforeAccessDB(req, listinfo.sql, listinfo.params, "list", this);
|
|
190
190
|
////如果返回Null,则表示不加载数据了
|
|
191
191
|
if (beforeData.canceled === true) return { successed: false, errorcode: 1, errmsg: "操作取消" };
|
|
192
192
|
////操作数据库
|
package/core/database/daoBase.js
CHANGED
|
@@ -101,17 +101,17 @@ class mysqlDao extends dao{
|
|
|
101
101
|
/**
|
|
102
102
|
* 删除记录
|
|
103
103
|
*/
|
|
104
|
-
deleteSql(
|
|
105
|
-
const ids = (id + '').trim().split(',').map(x => '\'' + x + '\'');
|
|
104
|
+
deleteSql() {
|
|
105
|
+
// const ids = (id + '').trim().split(',').map(x => '\'' + x + '\'');
|
|
106
106
|
////如果是逻辑删除,则只将记录的删除状态设置为1
|
|
107
107
|
if(this.tableoption.logicDelete) {
|
|
108
108
|
|
|
109
|
-
let sqlDelete = `update ${this.tableoption.tableName} set #DELETEBY# #DELETEDATE# ${this.tableoption.logicDeleteField || 'rec_isdeleted'} =1 where ${this.tableoption.primaryKey}
|
|
109
|
+
let sqlDelete = `update ${this.tableoption.tableName} set #DELETEBY# #DELETEDATE# ${this.tableoption.logicDeleteField || 'rec_isdeleted'} =1 where ${this.tableoption.primaryKey} =? ${this.tableoption.forcefilter}`;
|
|
110
110
|
sqlDelete = sqlDelete.replace('#DELETEBY#', this.tableoption.logDeleteBy?`${this.tableoption.logDeleteBy}=?,`:'')
|
|
111
111
|
sqlDelete = sqlDelete.replace('#DELETEDATE#', this.tableoption.logDeleteDate ? `${this.tableoption.logDeleteDate}=now(),` : '');
|
|
112
112
|
return sqlDelete;
|
|
113
113
|
}
|
|
114
|
-
return `delete from ${this.tableoption.tableName} where ${this.tableoption.primaryKey}
|
|
114
|
+
return `delete from ${this.tableoption.tableName} where ${this.tableoption.primaryKey} =? ${this.tableoption.forcefilter}`;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
/**
|
|
@@ -131,9 +131,9 @@ class mysqlDao extends dao{
|
|
|
131
131
|
async delete(Sql, id,userid) {
|
|
132
132
|
const ids = (id + '').trim().split(',').map(x => '\'' + x + '\'');
|
|
133
133
|
if (this.tableoption.logicDelete && this.tableoption.logDeleteBy){
|
|
134
|
-
return this.executeSql(Sql, [userid, ids.join(',')]);
|
|
134
|
+
return this.executeSql(Sql, [userid, id]); //ids.join(',')]);
|
|
135
135
|
}
|
|
136
|
-
return this.executeSql(Sql, ids.join(','));
|
|
136
|
+
return this.executeSql(Sql, id); //ids.join(','));
|
|
137
137
|
///
|
|
138
138
|
//return this.executeSql(Sql, id);
|
|
139
139
|
/*if (result.successed && this.tableoption.logicDelete && userid && (this.tableoption.logDeleteBy || this.tableoption.logDeleteDate)){
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doomiwork",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.4",
|
|
4
4
|
"description": "doomisoft nodejs web framework",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"license": "ISC",
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"express": "^4.17.1",
|
|
18
|
-
"moment": "^2.29.
|
|
18
|
+
"moment": "^2.29.4",
|
|
19
19
|
"mysql": "^2.18.1",
|
|
20
20
|
"node-uuid": "^1.4.8",
|
|
21
|
-
"node-xlsx": "^0.
|
|
21
|
+
"node-xlsx": "^0.24.0"
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -55,14 +55,14 @@ module.exports.parseKeyValue=(req,str)=>{
|
|
|
55
55
|
* @param {*} defaultValue 如果不满足,则返回默认的
|
|
56
56
|
* @returns
|
|
57
57
|
*/
|
|
58
|
-
module.exports.validatorParamsType=(value, dataType, defaultValue = null)=> {
|
|
58
|
+
module.exports.validatorParamsType = (value, dataType, defaultValue = null, inscope=[])=> {
|
|
59
59
|
if (!value || !dataType) return value;
|
|
60
60
|
switch (dataType.toLowerCase()) {
|
|
61
61
|
case 'guid': ///限制长度为36位的GUID
|
|
62
|
-
if (value.length != 36 || value.split('-').length != 5)
|
|
62
|
+
if (value.length != 36 || value.split('-').length != 5) value= defaultValue;
|
|
63
63
|
break;
|
|
64
64
|
case 'number': ///限制为数字
|
|
65
|
-
if (isNaN(value))
|
|
65
|
+
if (isNaN(value)) value= defaultValue;
|
|
66
66
|
break;
|
|
67
67
|
case 'date': ///限制为日期,格式输出为YYYY-MM-DD
|
|
68
68
|
let date = Date.parse(value);
|
|
@@ -72,8 +72,25 @@ module.exports.validatorParamsType=(value, dataType, defaultValue = null)=> {
|
|
|
72
72
|
let datetime = Date.parse(value);
|
|
73
73
|
if (isNaN(datetime)) return defaultValue;
|
|
74
74
|
return datetime;
|
|
75
|
+
case 'array': ///返回数组
|
|
76
|
+
value= value.split(',');
|
|
75
77
|
}
|
|
76
|
-
|
|
78
|
+
///检查值在不在范围内取值
|
|
79
|
+
let inRange = true;
|
|
80
|
+
if (inscope.length > 0 && value) {
|
|
81
|
+
inRange = false;
|
|
82
|
+
if (Array.isArray(value)) {
|
|
83
|
+
for(const item of value){
|
|
84
|
+
if (inscope.some(scope => scope.toLowerCase() === item.toLowerCase())){
|
|
85
|
+
inRange = true;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
inRange = inscope.some(scope => scope.toLowerCase() === value.toLowerCase())
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return inRange ? value : defaultValue;
|
|
77
94
|
}
|
|
78
95
|
|
|
79
96
|
/**
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
const { parseKeyValue, validatorParamsType } = require('./keywordparse');
|
|
2
|
+
const dataconfig = require('../configuration/dataconfig').getCurrent();
|
|
3
|
+
const mysql = require('mysql');
|
|
4
|
+
const ORDER_STRING = ['asc', 'desc']
|
|
5
|
+
const FORBID_SQL_KEYWORD = /;|(--)|(\bWHERE\b)|(\bSHOW\b)|(\bCOUNT\(\b)|(\bCREATE\b)|(\bCALL\b)|(\bBY\b)|(\bORDER\b)|(\bJOIN\b)|(\bUNION\b)|(\bFROM\b)|(\bSELECT\b)|(\bDROP\b)|(\bTRUNCATE\b)|(\bDELETE\b)|(\bUPDATE\b)|(\bINSERT\b)|(\bEXEC\b)|(\bEXECUTE\b)/gi;
|
|
6
|
+
|
|
7
|
+
function appendSearchCondition2Count(originSql, searchCondition) {
|
|
8
|
+
///如果包含了这个特定的字符,则替换
|
|
9
|
+
if (originSql.indexOf('#APPENDSEARCH#') >= 0)
|
|
10
|
+
return originSql.replace('#APPENDSEARCH#', searchCondition);
|
|
11
|
+
return originSql + ' ' + searchCondition;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 检查是否有Sql注入的风险
|
|
15
|
+
* @param {*} sql
|
|
16
|
+
*/
|
|
17
|
+
function checkSqlInjection(sql) {
|
|
18
|
+
if (!sql) return sql;
|
|
19
|
+
if (FORBID_SQL_KEYWORD.test(sql)) return '';
|
|
20
|
+
//let sqlError = sql.match(FORBID_SQL_KEYWORD); //strict ? sql.match(FORBID_SQL_KEYWORD_STRICT):sql.match(FORBID_SQL_KEYWORD);
|
|
21
|
+
//if (sqlError && sqlError.length > 0) return '';
|
|
22
|
+
return sql;
|
|
23
|
+
}
|
|
24
|
+
/*
|
|
25
|
+
* 根据对应列表的配置(dataConfig.list.search),从请求上下文中获取用户进行搜索的参数信息
|
|
26
|
+
*/
|
|
27
|
+
module.exports.getSearchCondition = (option) => {
|
|
28
|
+
let paraCopy = option || {};
|
|
29
|
+
if (!paraCopy.request || !paraCopy.refer) return '';
|
|
30
|
+
if (!paraCopy.valueFrom) paraCopy.valueFrom = "all";
|
|
31
|
+
const request = paraCopy.request;
|
|
32
|
+
let retSearch = [];
|
|
33
|
+
paraCopy.refer.forEach(element => {
|
|
34
|
+
retSearch.push(this.parseTagInSql(request, element.pattern, false));
|
|
35
|
+
});
|
|
36
|
+
return retSearch.join('');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 解析sql字符串中特殊的占位符
|
|
41
|
+
* @param {*} req
|
|
42
|
+
* @param {*} sql
|
|
43
|
+
* @param {*} allowNull
|
|
44
|
+
* @returns
|
|
45
|
+
*/
|
|
46
|
+
module.exports.parseTagInSql = (req, sql, allowNull = true) => {
|
|
47
|
+
if (!sql) return '';
|
|
48
|
+
///定义正则准备查找sql中的特定关键字
|
|
49
|
+
const matched = sql.match(/@.*?@/g);
|
|
50
|
+
if (!matched || matched.length <= 0) return sql;
|
|
51
|
+
let parseKeyWordIsNull = false;
|
|
52
|
+
|
|
53
|
+
const charReg = /\s+|:|=/gi
|
|
54
|
+
matched.forEach(ele => {
|
|
55
|
+
let matchValue = ele.substring(1, ele.length - 1);
|
|
56
|
+
///不允许特殊字符出现
|
|
57
|
+
let notdo = matchValue.match(charReg);
|
|
58
|
+
if (notdo && notdo.length > 0) return;
|
|
59
|
+
|
|
60
|
+
let noQuoteProtect = matchValue[0] == '!';
|
|
61
|
+
if (noQuoteProtect) {
|
|
62
|
+
matchValue = matchValue.substring(1);
|
|
63
|
+
}
|
|
64
|
+
///是否有格式要求
|
|
65
|
+
let validformat = matchValue.split('|');
|
|
66
|
+
matchValue = validformat[0];
|
|
67
|
+
let keyValue = (parseKeyValue(req, matchValue) || '')+''; //utility.ifNull(keyParse.parseKeyValue(req, matchValue), '')+'';
|
|
68
|
+
if (!keyValue) {
|
|
69
|
+
parseKeyWordIsNull = true;
|
|
70
|
+
} else if (typeof (keyValue) === 'string') {
|
|
71
|
+
keyValue = noQuoteProtect ? checkSqlInjection(mysql.escape(keyValue)) : mysql.escape(keyValue)
|
|
72
|
+
keyValue = keyValue.substr(1, keyValue.length - 2);
|
|
73
|
+
///验证参数的格式合法性
|
|
74
|
+
if (keyValue && validformat.length > 1) {
|
|
75
|
+
// console.log(`参数格式合法检查==>${validformat[1]}:${matchValue} = ${keyValue}`)
|
|
76
|
+
keyValue = validatorParamsType(keyValue, validformat[1], validformat[2])
|
|
77
|
+
}
|
|
78
|
+
if (!keyValue) {
|
|
79
|
+
parseKeyWordIsNull = true;
|
|
80
|
+
}
|
|
81
|
+
///没有引号保护下发现了sql注入,则用1=0不返回任何结果
|
|
82
|
+
if (!keyValue && noQuoteProtect) {
|
|
83
|
+
keyValue = '1=0'
|
|
84
|
+
}
|
|
85
|
+
} else { ////数组形式的参数,目前框架不支持,统一认为为sql注入攻击,全部忽略
|
|
86
|
+
// console.log(`参数非法==>类型${typeof (keyValue)}:${matchValue} = ${keyValue}`)
|
|
87
|
+
parseKeyWordIsNull = true;
|
|
88
|
+
keyValue = ''
|
|
89
|
+
}
|
|
90
|
+
sql = sql.replace(ele, keyValue);
|
|
91
|
+
});
|
|
92
|
+
if (!allowNull && parseKeyWordIsNull) return '';
|
|
93
|
+
return sql;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/*
|
|
97
|
+
* 列表请求上下文中获取需要的信息
|
|
98
|
+
* 如Page ,PageSize , Sort 等等
|
|
99
|
+
*/
|
|
100
|
+
module.exports.getListInfo = (req, dataKey, cfgType = 0, dao,ignorefilter=false) => {
|
|
101
|
+
if (!dataKey) return null;
|
|
102
|
+
///确认是否是需要导出Excel
|
|
103
|
+
const export2Excel = (req.query.exportexcel + "").toLowerCase() === "true";
|
|
104
|
+
let { page = 1, rows: pageSize = 100, sort, order, clientFilter: filter } = req.query
|
|
105
|
+
///防止sortSql 注入在排序的参数中
|
|
106
|
+
if (sort) sort = checkSqlInjection(sort);
|
|
107
|
+
// 防止页面数据传入非数字
|
|
108
|
+
if (isNaN(pageSize)) pageSize = 30;
|
|
109
|
+
// 防止页面数据传入非数字
|
|
110
|
+
if (!page || isNaN(page)) page = 1
|
|
111
|
+
///最大允许获取100条数据
|
|
112
|
+
pageSize =Math.max(0,Math.min(pageSize, 10000));
|
|
113
|
+
///拼接排序的语句
|
|
114
|
+
if (order && sort && ORDER_STRING.includes(order.toLowerCase())) sort = sort + ' ' + order
|
|
115
|
+
//req.order =req.sort? utility.ifNull((req.body.order || req.query.order), ""):'';
|
|
116
|
+
const dataConfig = dataconfig.getConfig(dataKey, cfgType);
|
|
117
|
+
if (dataConfig && dataConfig.list) {
|
|
118
|
+
req.dataConfig = dataConfig
|
|
119
|
+
let { sql, listsql, countsql, sqltype = 'sql', field, footer, search, sort: constsort } = dataConfig.list
|
|
120
|
+
/**解析查询条件 */
|
|
121
|
+
const searchCondition = ignorefilter?'':this.getSearchCondition({ request: req, refer: search });
|
|
122
|
+
/**排序方式 *///
|
|
123
|
+
sort = sort || constsort;
|
|
124
|
+
/**来自req请求参数中的过滤条件 */
|
|
125
|
+
let clientfilter = checkSqlInjection(this.parseTagInSql(req, filter, false));
|
|
126
|
+
//如果配置文件中不是直接的sql语句,则从DAO对象中的指定方法来获取sql
|
|
127
|
+
if (sqltype !== 'sql' && listsql && typeof (dao[listsql]) === 'function') {
|
|
128
|
+
sql = dao[sql](req, { page, rows: pageSize, sort, filter: searchCondition, client: clientfilter });
|
|
129
|
+
if (dao[countsql] && typeof (dao[countsql]) === 'function')
|
|
130
|
+
countsql = dao[countsql](req, { page, rows: pageSize, sort, filter: searchCondition, client: clientfilter });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**根据过滤条件、排序条件、分页获取的方式,和原始sql拼接成最终获取数据的sql */
|
|
134
|
+
if (sqltype==='sql'){
|
|
135
|
+
sql = this.parseTagInSql(req, sql) +
|
|
136
|
+
' ' + searchCondition + clientfilter +
|
|
137
|
+
(sort ? (' order by ' + sort) : '') +
|
|
138
|
+
(export2Excel ? '' : ' limit ' + Number(pageSize) + ' OFFSET ' + (Math.max(Number(page), 1) - 1) * Number(pageSize)) +
|
|
139
|
+
/////在Sql中再放入获取总记录数的语句
|
|
140
|
+
';SELECT FOUND_ROWS() AS total;';
|
|
141
|
+
/*** 如果存在汇总列的sql,则把SQL放置在最末尾 */
|
|
142
|
+
if (countsql) {
|
|
143
|
+
sql += appendSearchCondition2Count(this.parseTagInSql(req, countsql), searchCondition)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { sql, fields: field, footers: footer, page, hascounter: countsql?true:false };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/*
|
|
150
|
+
* 详细页面请求上下文中获取需要的信息
|
|
151
|
+
* 如Page ,PageSize , Sort 等等
|
|
152
|
+
*/
|
|
153
|
+
module.exports.getDetailInfo = (req,dataKey) => {
|
|
154
|
+
if (dataKey) {
|
|
155
|
+
const dataConfig = dataconfig.getConfig(dataKey);
|
|
156
|
+
if (dataConfig && dataConfig.detail) {
|
|
157
|
+
req.dataConfig = dataConfig;
|
|
158
|
+
const { primary, field: fields, primaryIsAutoIncrease } = dataConfig.detail;
|
|
159
|
+
return { primary, fields, autoincrease: primaryIsAutoIncrease }
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const dataconfig = require('../configuration/dataconfig').getCurrent();
|
|
3
3
|
const mysql = require('mysql');
|
|
4
4
|
const ORDER_STRING = ['asc', 'desc']
|
|
5
|
-
const FORBID_SQL_KEYWORD = /;|(--)|(\bWHERE\b)|(\bSHOW\b)|(\bCOUNT\(\b)|(\bCREATE\b)|(\bCALL\b)|(\bBY\b)|(\bORDER\b)|(\bJOIN\b)|(\bUNION\b)|(\bFROM\b)|(\bSELECT\b)|(\bDROP\b)|(\bTRUNCATE\b)|(\bDELETE\b)|(\bUPDATE\b)|(\bINSERT\b)|(\bEXEC\b)|(\bEXECUTE\b)/gi;
|
|
5
|
+
const FORBID_SQL_KEYWORD = /;|(--)|(\bWHERE\b)|(\bCASE\b)|(\bWHEN\b)|(\bSLEEP\b)|(\bSHOW\b)|(\bCOUNT\(\b)|(\bCREATE\b)|(\bCALL\b)|(\bBY\b)|(\bORDER\b)|(\bJOIN\b)|(\bUNION\b)|(\bFROM\b)|(\bSELECT\b)|(\bDROP\b)|(\bTRUNCATE\b)|(\bDELETE\b)|(\bUPDATE\b)|(\bINSERT\b)|(\bEXEC\b)|(\bEXECUTE\b)/gi;
|
|
6
6
|
|
|
7
7
|
function appendSearchCondition2Count(originSql, searchCondition) {
|
|
8
8
|
///如果包含了这个特定的字符,则替换
|
|
@@ -16,8 +16,7 @@ function appendSearchCondition2Count(originSql, searchCondition) {
|
|
|
16
16
|
*/
|
|
17
17
|
function checkSqlInjection(sql) {
|
|
18
18
|
if (!sql) return sql;
|
|
19
|
-
|
|
20
|
-
if (sqlError && sqlError.length > 0) return '';
|
|
19
|
+
if (FORBID_SQL_KEYWORD.test(sql)) return '';
|
|
21
20
|
return sql;
|
|
22
21
|
}
|
|
23
22
|
/*
|
|
@@ -28,11 +27,16 @@ module.exports.getSearchCondition = (option) => {
|
|
|
28
27
|
if (!paraCopy.request || !paraCopy.refer) return '';
|
|
29
28
|
if (!paraCopy.valueFrom) paraCopy.valueFrom = "all";
|
|
30
29
|
const request = paraCopy.request;
|
|
31
|
-
let retSearch = [];
|
|
30
|
+
let retSearch = [],params = [];
|
|
32
31
|
paraCopy.refer.forEach(element => {
|
|
33
|
-
|
|
32
|
+
const paramSql = this.parseTagForParameterize(request, element);
|
|
33
|
+
if (paramSql.sql) {
|
|
34
|
+
retSearch.push(paramSql.sql);
|
|
35
|
+
params = params.concat(paramSql.params||[]);
|
|
36
|
+
}
|
|
34
37
|
});
|
|
35
|
-
return
|
|
38
|
+
return {filter:retSearch.join(''),params}
|
|
39
|
+
//return retSearch.join('');
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
/**
|
|
@@ -42,13 +46,12 @@ module.exports.getSearchCondition = (option) => {
|
|
|
42
46
|
* @param {*} allowNull
|
|
43
47
|
* @returns
|
|
44
48
|
*/
|
|
45
|
-
module.exports.parseTagInSql = (req, sql, allowNull = true
|
|
49
|
+
module.exports.parseTagInSql = (req, sql, allowNull = true) => {
|
|
46
50
|
if (!sql) return '';
|
|
47
51
|
///定义正则准备查找sql中的特定关键字
|
|
48
52
|
const matched = sql.match(/@.*?@/g);
|
|
49
53
|
if (!matched || matched.length <= 0) return sql;
|
|
50
54
|
let parseKeyWordIsNull = false;
|
|
51
|
-
|
|
52
55
|
const charReg = /\s+|:|=/gi
|
|
53
56
|
matched.forEach(ele => {
|
|
54
57
|
let matchValue = ele.substring(1, ele.length - 1);
|
|
@@ -67,20 +70,15 @@ module.exports.parseTagInSql = (req, sql, allowNull = true,isSearch=false) => {
|
|
|
67
70
|
if (!keyValue) {
|
|
68
71
|
parseKeyWordIsNull = true;
|
|
69
72
|
} else if (typeof (keyValue) === 'string') {
|
|
70
|
-
keyValue = noQuoteProtect
|
|
71
|
-
keyValue = keyValue.
|
|
73
|
+
keyValue = noQuoteProtect ? checkSqlInjection(mysql.escape(keyValue)) : mysql.escape(keyValue)
|
|
74
|
+
keyValue = keyValue.substring(1, keyValue.length - 1);
|
|
72
75
|
///验证参数的格式合法性
|
|
73
|
-
if (keyValue && validformat.length > 1)
|
|
74
|
-
// console.log(`参数格式合法检查==>${validformat[1]}:${matchValue} = ${keyValue}`)
|
|
75
|
-
keyValue = validatorParamsType(keyValue, validformat[1], validformat[2])
|
|
76
|
-
}
|
|
76
|
+
if (keyValue && validformat.length > 1) keyValue = validatorParamsType(keyValue, validformat[1], validformat[2])
|
|
77
77
|
if (!keyValue) {
|
|
78
78
|
parseKeyWordIsNull = true;
|
|
79
79
|
}
|
|
80
80
|
///没有引号保护下发现了sql注入,则用1=0不返回任何结果
|
|
81
|
-
if (!keyValue && noQuoteProtect)
|
|
82
|
-
keyValue = '1=0'
|
|
83
|
-
}
|
|
81
|
+
if (!keyValue && noQuoteProtect) keyValue = '1=0'
|
|
84
82
|
} else { ////数组形式的参数,目前框架不支持,统一认为为sql注入攻击,全部忽略
|
|
85
83
|
// console.log(`参数非法==>类型${typeof (keyValue)}:${matchValue} = ${keyValue}`)
|
|
86
84
|
parseKeyWordIsNull = true;
|
|
@@ -92,6 +90,54 @@ module.exports.parseTagInSql = (req, sql, allowNull = true,isSearch=false) => {
|
|
|
92
90
|
return sql;
|
|
93
91
|
}
|
|
94
92
|
|
|
93
|
+
/**
|
|
94
|
+
* 为列表查询定义参数化查询
|
|
95
|
+
* @param {*} req
|
|
96
|
+
* @param {*} sql
|
|
97
|
+
* @param {*} allowNull
|
|
98
|
+
* @param {*} isSearch
|
|
99
|
+
* @returns
|
|
100
|
+
*/
|
|
101
|
+
module.exports.parseTagForParameterize = (req, filterSetting={}) => {
|
|
102
|
+
///type=parameter表示参数化方式查询
|
|
103
|
+
///type=joint 表示拼接sql语句查询
|
|
104
|
+
///scope=[] 表示需要解析的字段必须在此列表内容中
|
|
105
|
+
let {pattern:sql,type='parameter',inscope=[]} = filterSetting;
|
|
106
|
+
if (!sql) return {sql:''};
|
|
107
|
+
///定义正则准备查找sql中的特定关键字
|
|
108
|
+
const matched = sql.match(/@.*?@/g);
|
|
109
|
+
let params = [];
|
|
110
|
+
if (!matched || matched.length <= 0) return {sql};
|
|
111
|
+
for(const ele of matched){
|
|
112
|
+
let matchValue = ele.substring(1, ele.length - 1);
|
|
113
|
+
///检查是否有特殊的字符比如 % 等
|
|
114
|
+
const specialRegex = /^[^a-zA-Z0-9]*(.*?)[^a-zA-Z0-9]*$/
|
|
115
|
+
const matchContent = matchValue.match(specialRegex);
|
|
116
|
+
matchValue = matchContent ? matchContent[1]:matchValue;
|
|
117
|
+
if (!matchValue) return {sql:'',params:[]};
|
|
118
|
+
///是否有格式要求
|
|
119
|
+
let validformat = matchValue.split('|');
|
|
120
|
+
matchValue = validformat[0];
|
|
121
|
+
let keyValue = parseKeyValue(req, matchValue)||''; //utility.ifNull(keyParse.parseKeyValue(req, matchValue), '')+'';
|
|
122
|
+
if (keyValue && validformat.length > 1) keyValue = validatorParamsType(keyValue, validformat[1], validformat[2], inscope)
|
|
123
|
+
///如果解析不出这个KeyValue ,则认为当前这条SQL过滤无效
|
|
124
|
+
if (!keyValue) return {sql:'',params:[]};
|
|
125
|
+
if (keyValue) {
|
|
126
|
+
if(type.toLowerCase() === 'parameter'){
|
|
127
|
+
sql = sql.replace(ele, '?'); ///变成参数化查询
|
|
128
|
+
if (Array.isArray(keyValue))
|
|
129
|
+
params.push(keyValue)
|
|
130
|
+
else
|
|
131
|
+
params.push(matchContent[0].replace(matchContent[1], keyValue));// '%' + keyValue + '%')
|
|
132
|
+
} else if (type.toLowerCase() === 'joint'){ ///拼接sql语句查询
|
|
133
|
+
///拼接的SQL语句,keyvalue必须在scope列表中,否则不予拼接
|
|
134
|
+
const result = inscope.some(item => item.toLowerCase() === matchValue.toLowerCase());
|
|
135
|
+
if (result)sql = sql.replace(ele, keyValue);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return {sql,params};
|
|
140
|
+
}
|
|
95
141
|
/*
|
|
96
142
|
* 列表请求上下文中获取需要的信息
|
|
97
143
|
* 如Page ,PageSize , Sort 等等
|
|
@@ -124,25 +170,27 @@ module.exports.getListInfo = (req, dataKey, cfgType = 0, dao) => {
|
|
|
124
170
|
let clientfilter = checkSqlInjection(this.parseTagInSql(req, filter, false));
|
|
125
171
|
//如果配置文件中不是直接的sql语句,则从DAO对象中的指定方法来获取sql
|
|
126
172
|
if (sqltype !== 'sql' && listsql && typeof (dao[listsql]) === 'function') {
|
|
127
|
-
sql = dao[sql](req, { page, rows: pageSize, sort, filter: searchCondition, client: clientfilter });
|
|
173
|
+
sql = dao[sql](req, { page, rows: pageSize, sort, filter: searchCondition.filter, client: clientfilter });
|
|
128
174
|
if (dao[countsql] && typeof (dao[countsql]) === 'function')
|
|
129
|
-
countsql = dao[countsql](req, { page, rows: pageSize, sort, filter: searchCondition, client: clientfilter });
|
|
175
|
+
countsql = dao[countsql](req, { page, rows: pageSize, sort, filter: searchCondition.filter, client: clientfilter });
|
|
130
176
|
}
|
|
131
177
|
|
|
132
178
|
/**根据过滤条件、排序条件、分页获取的方式,和原始sql拼接成最终获取数据的sql */
|
|
133
179
|
if (sqltype==='sql'){
|
|
134
180
|
sql = this.parseTagInSql(req, sql) +
|
|
135
|
-
' ' + searchCondition + clientfilter +
|
|
181
|
+
' ' + searchCondition.filter + clientfilter +
|
|
136
182
|
(sort ? (' order by ' + sort) : '') +
|
|
137
183
|
(export2Excel ? '' : ' limit ' + Number(pageSize) + ' OFFSET ' + (Math.max(Number(page), 1) - 1) * Number(pageSize)) +
|
|
138
184
|
/////在Sql中再放入获取总记录数的语句
|
|
139
185
|
';SELECT FOUND_ROWS() AS total;';
|
|
140
186
|
/*** 如果存在汇总列的sql,则把SQL放置在最末尾 */
|
|
141
187
|
if (countsql) {
|
|
142
|
-
sql += appendSearchCondition2Count(this.parseTagInSql(req, countsql), searchCondition)
|
|
188
|
+
sql += appendSearchCondition2Count(this.parseTagInSql(req, countsql), searchCondition.filter)
|
|
143
189
|
}
|
|
144
190
|
}
|
|
145
|
-
|
|
191
|
+
///如果存在统计的SQL,则参数需要Double一次
|
|
192
|
+
const sqlParams = countsql ? [].concat(searchCondition.params, searchCondition.params):searchCondition.params;
|
|
193
|
+
return { sql, fields: field, params: sqlParams, footers: footer, page, hascounter: countsql?true:false };
|
|
146
194
|
}
|
|
147
195
|
}
|
|
148
196
|
/*
|