今天闲来无事升级了下项目中的jar包,把Druid从1.2.20升级到了1.2.23,想着只是一个小版本号的升级,应该不会有啥问题,本地盲测了一下,没想到竟然出幺蛾子了。
com.jfinal.plugin.activerecord.ActiveRecordException: java.sql.SQLException: sql injection violation, dbType mysql, druid-version 1.2.23, select alway true condition not allow : select count(*) from sys_user_login a left join sys_user b on b.id = a.user_id where 1=1 at com.alibaba.druid.wall.WallFilter.checkInternal(WallFilter.java:883) ~[druid-1.2.23.jar:?] at com.alibaba.druid.wall.WallFilter.connection_prepareStatement(WallFilter.java:318) ~[druid-1.2.23.jar:?] at com.alibaba.druid.filter.FilterChainImpl.connection_prepareStatement(FilterChainImpl.java:547) ~[druid-1.2.23.jar:?] at com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl.prepareStatement(ConnectionProxyImpl.java:328) ~[druid-1.2.23.jar:?] at com.alibaba.druid.pool.DruidPooledConnection.prepareStatement(DruidPooledConnection.java:369) ~[druid-1.2.23.jar:?] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_172] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_172] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_172] at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_172]
核心的错误其实就这一句了:select alway true condition not allow。意思就是说where后面不允许出现永真的条件,比如1=1。
1=1的写法多用于多条件动态查询,是为了解决拼接and时不致于出现语法问题。但是从报错看,是不允许这么玩了。于是查了下资料。
网上有三种解决方案吧:一、避免使用1=1,但是我现在来不及改。二、取消wall防火墙,但是我认为取消后会影响安全性。三、wallConfig.setConditionAndAlwayTrueAllow(true);试了不管用,而且我发现人家默认就是true。
没办法,以上方法均不可行,于是自己查Druid的源码,发现了以下问题。
问题一:Druid的永真检测各版本一直以来都是默认开启的,但是之前的版本中为什么没有生效?这竟然是长期以来的一个bug!
传送门:https://github.com/qxo/druid/commits/refs/heads/fix-wall-deleteWhereAlwaysTrue/
我一开始比对两个版本的源代码时发现,核心的判断逻辑确实不一样,还以为改了设计,原来是之前的逻辑写错了。_(¦3」∠)_
问题二:从上面的源代码中可以看出,永真检测确实可以关闭,但不是针对conditionAndAlwayTrueAllow这个参数,而是selectWhereAlwayTrueCheck这个参数。
wall防火墙的其他配置,我发现了一篇文章,写的很全,分享一下:https://www.cnblogs.com/chenglc/p/9983799.html
解决方案
所以,在jfinal中如何关闭druid的永真检测?下面上方案。
public void configPlugin(Plugins me) { DruidPlugin druidPlugin = getDruidPlugin(); wallFilter = new WallFilter(); wallFilter.setDbType("mysql"); // 关闭永真检测 WallConfig wallConfig = new WallConfig(); wallConfig.setSelectWhereAlwayTrueCheck(false); wallFilter.setConfig(wallConfig); //增加过滤器 druidPlugin.addFilter(wallFilter); druidPlugin.addFilter(new StatFilter()); me.add(druidPlugin); ...... }
写在最后
说到底,都是为了解决多参数动态查询问题,我现在的sql都是写在外部文件中的,通过isNotBlank来动态拼接查询语句。大家是如何解决这个动态查询问题的,有没有更优雅的解决方案?