mybatis中#{}与${}区别

共 1820字,需浏览 4分钟

 ·

2021-01-11 22:40

点击上方蓝色字体,选择“标星公众号”

优质文章,第一时间送达

  作者 |  xuanm

来源 |  urlify.cn/63E3yq

76套java从入门到精通实战课程分享

mybatis 中使用 sqlMap 进行 sql 查询时,经常需要动态传递参数,例如sql 如下:

select * from student where uid=#{uid} AND student_name='${studentName}'

在动态 SQL 解析阶段, #{ } 和 ${ } 会有不同的表现:

#{ } 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符占位符 ?。

上面的例子就被解析为

...... uid=? 

${ } 仅仅为一个纯碎的 string 替换,在mybatis的动态 SQL 解析阶段将会进行变量替换。比如:传入的studentName参数是“张三”

...... student_name='张三'

最后解析完成的完整语句就是

select * from student where uid= ? AND student_name='张三'

然后把解析好的语句在通过JDBC执行。

 

所以因为在${ } 在预编译之前已经被变量替换了,这会存在 sql 注入问题。比如:传入的studentName参数是“1' OR '1'='1

在mybatis中解析完成后变成了

select * from student where uid= ? AND student_name='1' OR '1'='1'

这样就查询出所有的数据了。

源码查看:

在执行mybtis的查询是,会执行到PreparedStatementHandler#query,查看生成的PreparedStatement

 

 如果传入参数传入的studentName参数是“1' OR '1'='1

 

 所以就出现注入的情况了。

如果我们把${studentName}换成#{studentName},在传入“1' OR '1'='1”呢

在mybatis解析出的结果

 

 在数据库的执行日志中查询执行的sql语句

 发现我们传入的“'”被转义了,

 所以通过预编译,用占位符的方式传值可以把一些特殊的字符进行转义,这样可以防止一些sql注入。

其原因就是:采用了JDBC的PreparedStatement,就会将sql语句:“select * from student where uid= ? AND student_name= ?” 预先编译好,也就是SQL引擎会预先进行语法分析,产生语法树,生成执行计划,也就是说,后面你输入的参数,无论你输入的是什么,都不会影响该SQL语句的语法结构了,因为语法分析已经完成了,而语法分析主要是分析sql命令,比如select,from,where,and,or,order by等等。所以即使你后面输入了这些sql命令,也不会被当成sql命令来执行了,因为这些SQL命令的执行,必须先得通过语法分析,生成执行计划,既然语法分析已经完成,已经预编译过了,那么后面输入的参数,是绝对不可能作为SQL命令来执行的,只会被当做字符串字面值参数。所以的sql语句预编译可以防御SQL注入。而且在多次执行同一个SQL时,能够提高效率。原因是SQL已编译好,再次执行时无需再编译。

默认使用PreparedStatement是不能执行预编译的,这需要在url中给出useServerPrepStmts=true参数(MySQL Server 4.1之前的版本是不支持预编译的,而Connector/J在5.0.5以后的版本,默认是没有开启预编译功能的)。

例如:jdbc:mysql://localhost:3306/test?useServerPrepStmts=true

这样才能保证mysql驱动会先把SQL语句发送给服务器进行预编译,然后在执行executeQuery()时只是把参数发送给服务器。




粉丝福利:Java从入门到入土学习路线图

👇👇👇

👆长按上方微信二维码 2 秒


感谢点赞支持下哈 

浏览 20
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报