<返回更多

mybatis中#{}与${}区别

2021-01-12    
加入收藏

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

mybatis中#{}与${}区别

 

 

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

mybatis中#{}与${}区别

 

 

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

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

在mybatis解析出的结果

mybatis中#{}与${}区别

 


mybatis中#{}与${}区别

 

 

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

mybatis中#{}与${}区别

 

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

所以通过预编译,用占位符的方式传值可以把一些特殊的字符进行转义,这样可以防止一些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()时只是把参数发送给服务器。

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>