2023年7月3日发(作者:)
wireshark抓包分析mybatis的sql参数化查询 我们使⽤jdbc操作数据库的时候,都习惯性地使⽤参数化的sql与数据库交互。因为参数化的sql有两⼤有点,其⼀,防⽌sql注⼊;其⼆,提⾼sql的执⾏性能(同⼀个connection共⽤⼀个的sql编译结果)。下⾯我们就通过mybatis来分析⼀下参数化sql的过程,以及和⾮参数化sql的不同。 注意: ①本次使⽤wireshark来监听⽹卡的请求,测试过程中,如果使⽤的是本地的mysql的话,java和mysql的交互是不需要经过wireshark的,所以如果是想⽤wireshark监听⽹卡的请求,推荐是链接远程的数据库。 ②本⽂的项⽬源代码在⽂章末尾有链接(项⽬源代码中也有设计的表的sql)。 ③可以结合wiereshark的抓包和mysql的general_log⼀起来查看sql的参数化过程,⽂章末尾会贴上从mysql的general_log⾓度检测到useServerPrepStmts=true/false两种执⾏⽅式的区别。 ⼀开始,项⽬中我的db配置如下,我们就先⽤这个配置来测试⼀下。 jdbc:mysql://:3306/test?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai 如下
发送完sql模板之后,从response中拿到statement id之后,紧跟着就发送参数和statement id到mysql执⾏引擎,wireshark抓到的数据如下: 如此,便可实现sql的参数化查询。按照理解,如果此时再⽤此sql模板查询另外⼀个user_id的数据,理论上是不需要再发送sql模板到mysql服务器了的,只需要发送参数和statement ID就可以了的,下⾯我就试⼀下,测试⽤例如下: @Test public void findByPk() throws IOException { String resource = ""; InputStream inputStream = ourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); try (SqlSession session = ssion()) { UserMapper mapper = per(); User user = Name("SYS_APP_d5bwx8AfZXkKewMkOkaZhBv7MWqjiDX3qIkfPkG8"); if (user != null || user == null) { user = Name("SYS_APP_d5bwx8AfZXkKewMkOkaZhBv7MWqjiDX3qIkfPkG8"); } n(user); } } 执⾏测试⽤例,⽤wireshark抓包,如下: 通过上图发现,怎么第⼆个findByName,压根就没发请求到mysql服务器,原来是因为本地的jdbc发现是相同的查询,直接返回了上⼀个查询的结果,所以不需要重新到mysql服务器去请求数据。那我在第⼆个findByName改⼀个和第⼀个不⼀样的参数,测试⽤例如下: @Test public void findByPk() throws IOException { String resource = ""; InputStream inputStream = ourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); try (SqlSession session = ssion()) { UserMapper mapper = per(); User user = Name("SYS_APP_d5bwx8AfZXkKewMkOkaZhBv7MWqjiDX3qIkfPkG8"); if (user != null || user == null) { user = Name("SYS_APP_d5bwx8AfZXkKewMkOkaZhBv7MWqjiDX3qIkfPkG9"); } n(user); } } 执⾏上⾯这个测试⽤例,wireshark抓包结果如下: 上图发现,竟然两次请求都重复发送了模板sql到mysql服务器预编译,为何呢?原来db的url配置⾥⾯还漏了⼀个属性配置(cachePrepStmts),增加cachePrepStmts配置之后,db的url配置如下:jdbc:mysql://:3306/test?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&useServerPrepStmts=true&cachePrepStmts=true 更新db的url配置之后,再执⾏测试⽤例,wireshark抓包结果如下: 通过上图发现,第⼆次findByName不再发送模板sql了,直接就是发送Execute Statement了,其实Execute Statement就是执⾏sql的参数和statement ID(该connection第⼀次预编译模板sq的时候l返回的)。但是中间还是会发⼀个Reset Statement的mysql数据包,为什么要发这个Reset Statement数据包,有知道的同学,可以评论回复⼀下,我也还没去深究~谢谢~ 这⾥附带再说⼀下mybatis的参数化sql可以防⽌sql注⼊的理解,其实防⽌sql注⼊,有两点,其⼀,mybatis本⾝会有⼀个sql参数化的过程,这⾥涉及到mybatis的#和$的区别,参数化sql是⽤#引⽤变量,mybatis会对参数进⾏特殊字符以及敏感字符的转义以防⽌sql注⼊;其⼆,db的url配置中加了useServerPrepStmts=true之后,mysql服务端会对Execute Statement发送的参数中涉及的敏感字符进⾏转义,以防⽌sql注⼊,所以,如果不加useServerPrepStmts=true的话,会发现,mybatis在本地就已经对参数中涉及的敏感字符进⾏了转义之后,再发往mysql server,可以使⽤wireshark抓包看到;但是如果是加了useServerPrepStmts=true之后,会发现client发往mysql server的参数(Execute Statement),mybatis不会对其中的参数进⾏转义了,参数敏感字符转义这⼀块交给了mysql server去做,也可以通过wireshark抓包看到。so,这⾥会有两块地⽅防⽌sql注⼊,⼀块在client,⼀块在mysql server(使⽤存储过程防⽌sql注⼊也是使⽤了mysql server的该功能),就看你是否使⽤useServerPrepStmts。
附录: verPrepStmts=false/true,wireshark抓包结果 useServerPrepStmts=false,wireshark抓包结果如下: useServerPrepStmts=true,wireshark抓包结果如下:
2. mysql server的general_log⾓度检测到useServerPrepStmts=false/true的执⾏sql useServerPrepStmts=false的general_log 2019-08-18T15:19:12.330744Z 38 Query SET autocommit=02019-08-18T15:19:12.345704Z 38 Query select * from `user` where user_id = 'SYS_APP_d5bwx8AfZXkKewMkOkaZhBv7MWqjiDX3qIkfPkG8' or 1 = 1 #'2019-08-18T15:19:12.358669Z 38 Query select * from `user` where user_id = 'SYS_APP_d5bwx8AfZXkKewMkOkaZhBv7MWqjiDX3qIkfPkG9'2019-08-18T15:19:12.359666Z 38 Query SET autocommit=1
useServerPrepStmts=true的general_log
2019-08-18T09:39:42.533289Z 30 Query SET autocommit=02019-08-18T09:39:42.546254Z 30 Prepare select * from `user` where user_id = ?2019-08-18T09:39:42.550244Z 30 Execute select * from `user` where user_id = 'SYS_APP_d5bwx8AfZXkKewMkOkaZhBv7MWqjiDX3qIkfPkG8' or 1 = 1 #'2019-08-18T09:39:42.560217Z 30 Reset stmt
2019-08-18T09:39:42.561214Z 30 Execute select * from `user` where user_id = 'SYS_APP_d5bwx8AfZXkKewMkOkaZhBv7MWqjiDX3qIkfPkG9'2019-08-18T09:39:42.563210Z 30 Query SET autocommit=1
发布者:admin,转转请注明出处:http://www.yc00.com/web/1688342145a123530.html
评论列表(0条)