改造说明
前端拼接sql语句(主要是filterItems)存在SQL注入的风险,因此做本次改造以避免风险
处理方式
框架提供三种方式用于处理SQL条件子句:标准格式、自定义解析类、传递queryID
标准格式
通过前端提供的接口,将filterItems由SQL子句的格式改造成json格式。替换以前的格式传给各个filterItems(包括表格,F7,通用查询等内置控件),框架后台会自动完成解析
接口包括waf.parseSql.getFilter,waf.parseSql.mergeFilter,waf.parseSql.deleteFilter,waf.parseSql.replaceFilter,waf.parseSql.getValue。下面以id='CeUAAAAAlCnM567U'(例1)和id='CeUAAAAAlCnM567U' and name like ’%bob%’ (例2)为例具体说明接口的参数和用法
工具类(前端)
获取
waf.parseSql.getFilter是获取sql语句的接口,接收四个参数(keys,links,values, joins)
keys:字符串或数组,对应filter片段中的左变量(样例中即为'id'或['id', 'name'])
links: 字符串或数组,对应filter片段中的连接符(样例中即为'='或['=', 'like'])
values:字符串或数组,对应filter片段中的值(样例中即为'CeUAAAAAlCnM567U'或[‘CeUAAAAAlCnM567U’,’ %bob%’])
joins:字符串,如果为样例1单语句拼接不需要传值,为样例2时需要传多个条件的连接关系(样例中即为'and')
注:若数组长度大于等于3,joins需传多个连接符时格式为#1and#2or#3and#4,不支持()
合并
waf.parseSql.mergeFilter是合并两条语句的接口,接收两个参数(subjects,joins),主要是对getFilter接口中不支持小括号()的补充
subjects:数组,元素为通过getFilter接口构造出来的filter片段
joins:参考getFilter中说明
删除
waf.parseSql.deleteFilter是删除语句中某个片段的接口,接收两个参数(filter,args)
filter:源sql语句(改造之后的格式)
args:数组,长度小于等于3 。三个位置分别匹配getFilter中构造时的左变量,连接符和值,其中左变量未必传,后面两个参数如果有值则匹配否则忽略
替换
waf.parseSql.replaceFilter替换语句中某项的值,接收三个参数(filter,args1,args2),
filter:源sql语句(改造之后的格式)
args1:数组,长度小于等于3,分别为left(必传)和link,right(非必传)
args2:数组,需要替换的条件,长度小于等于3,如果维持不变可在对应位置传undefined,其他均会替换
查询变量值
waf.parseSql.getValue获取某个变量的值,接收两个参数(filter,args)
filter:源sql语句(改造之后的格式)
args:数组,第一个参数为left(必传),第二个参数为link(若不传则默认匹配第一个left符合的)
注:1.以上前端js接口均以返回值的方式返回结果,不会修改传入的参数
2. deleteFilter,replaceFilter和getValue三个接口使用时需要多测试避免出问题
案例
在前端构造filter时以前的通用方式是company.id='CeUAAAAAlCnM567U' and ((billDate>='2017-08-01') and (billDate<'2017-09-01'))。
改造的方式可以把这个语句看成三部分:company.id='CeUAAAAAlCnM567U',billDate>='2017-08-01'和billDate<'2017-09-01',可以分三步完成:
- 调用getFilter接口获取company.id='CeUAAAAAlCnM567U'对应的格式,var orgFilter=waf.parseSql.getFilter("company.id","=","CeUAAAAAlCnM567U")
- 调用getFilter接口获取(billDate>='2017-08-01') and (billDate<'2017-09-01')对应的格式var dateFilter= waf.parseSql.getFilter(["billDate","billDate"],[">=","<"],["2017-08-01","2017-09-01"],"and")或var dateFilter= waf.parseSql.getFilter("billDate",[">=","<"],["2017-08-01","2017-09-01"],"and")
注:为了避免重复写前两个参数,左变量和连接符如果全部相同的话可以只传一次字符串,但是最后的值一定不能只传一个
- 调用mergeFilter接口连接1,2中的结果var filter= waf.parseSql. mergeFilter([orgFilter, dateFilter],”and”)获取的结果即可
注:以上过程由于是三个and连接实际上后两个条件的()可以忽略,因此也可以只调用一次getFilter接口:var filter= waf.parseSql.getFilter(" company.id", "billDate","billDate"],["=",">=","<"],["CeUAAAAAlCnM567U","2017-08-01","2017-09-01"],"and")
自定义解析类
部分业务场景会涉及子查询或其他复杂filter拼接无法以标准格式传递,需要自定义解析类
工具类(后端)
接口com.kingdee.bos.webframework.sql.ISqlLoader
默认实现类com.kingdee.bos.webframework.sql.DefaultSqlLoader
案例
在配置页面填写完整类路径,如下图:
自定义的解析类必须实现ISqlLoader接口,建议可以继承框架内置的解析类DefaultSqlLoader使用super的方式回调以解析标准的filter
如图中所示,接口中供业务复写的方法是getFilterItems(String filterItems,String[] queryIDs),具体实现可以参考标准产品中的com.kingdee.eas.cp.bc.dynamic.utils.BizEditSqlLoader类
传递queryID
页面初始化时,业务代码在后台构造SQL条件以参数形式传到前端页面,页面上再设置到各个控件(表格,F7,通用查询)或在js代码中使用。这种场景采用后台存储SQL,前后端交互只传递queryID的方式来处理
工具类(后端)
工具类com.kingdee.bos.webframework.sql.SqlUtils
生成queryID
SqlUtils. getIdByFilter(String filter,HttpServletRequest request)
filter为后台拼接的SQL子句,调用该接口后可生成queryID
还原SQL子句
SqlUtils.getFilterById(String id,HttpServletRequest request)
id为queryID,调用该接口获得对应的SQL子句
案例
在后台调用SqlUtils.getIdByFilter(String filter,HttpServletRequest request)接口获取queryID,将queryID传递到前端,赋值给相应框架控件的queryID属性,框架后台解析filter时类自动取出queryID对应的filter进行拼接。如果是以2中自定义解析类的方式也可以自行调用SqlUtils的getFilterById(String id,HttpServletRequest request)方法获取filter完成拼接。
注:1.针对部分反应SqlUtils方法中request中接口无法获取的问题,在web工程项目中可以从两个地方拿到:各handler的event参数中或controller中spring注入的request;通过(HttpServletRequest) com.kingdee.bos.webframework.context.WafContext.getInstance().getRequest()方式获取;
2.复杂查询的情况在web列表页面中除配置自定义解析类的方式也可以配置页面listdata的前插入事件,从request中取出filterItems经过自定义处理之后把解析的结果放到modelMap中(键也为filterItems),走框架解析时会优先取modelMap处理过的条件
补充说明
扩展使用
框架目前只内置了对filterItems的处理,同时后台检测敏感字符时也只针对了filterItems参数,如果具体业务有其他参数需求可以修改server\deploy\easweb.ear\eas_web.war\WEB-INF\properties\waf2.properties文件中checkParams参数添加需要自动检测的参数,修改checkKeys参数中需要额外过滤出现的关键字符(此参数中出现的#表明过滤的是关键字后接任意个控件,即以其他字符串形式出现的不被拦截,配置参数时也需避免以关键字结尾)
兼容模式
考虑到部分产品改造时间会比较长,框架提供了兼容模式,支持改造后的标准格式和改造前的格式混用。server\deploy\easweb.ear\eas_web.war\WEB-INF\properties\waf2.properties中compatible的值为true时开启兼容模式(默认为true)。注意,兼容模式中sql的左变量和值中不能出现{和},否则解析会出错。并且开启时只是在解析会分别解析两种格式,仍然不能对改造之后的格式进行破坏操作(例如substring之类的截取操作)
调试
调试时会出现sql语句不直观,queryID对应的条件无法查看等问题。框架提供了工具用于查询filterItems、queryID、自定义解析类,工具地址如下:
http://ip:port/easweb/dynamicPage.do?event=initialize&method=doEvent&uipk=com.kingdee.eas.webDev.webDevTools&waf2skin=easbase&inwafpage=false&debug=true
其中filterItems,queryID即为相应参数不赘述,uipk是针对配置了自定义解析类的页面需要加上。结果中第一个boolean类型为能否通过关键字过滤检测,剩下部分为sql在后台得到的翻译结果。