首先咱们先看#{}收参的代码及执行结果:
<select id="selectUserByName" resultType="com.zf.entity.User">
select * from t_users where name=#{name}
</select>
@Test
public void selectUserByName() throws IOException {
UserDao userDao = MybatisUtils.getMapper(UserDao.class);
User user = userDao.selectUserByName("zeor");
System.out.println(user);
}
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - ==> Preparing: select * from t_users where name=?
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - ==> Parameters: zeor(String)
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - <== Total: 1
完整SQL语名:select * from t_users where name="zeor"
然后咱们再看${}的收参方式执行的代码和执行结果
<select id="selectUserByName" resultType="com.zf.entity.User">
select * from t_users where name=${name}
</select>
@Test
public void selectUserByName() throws IOException {
UserDao userDao = MybatisUtils.getMapper(UserDao.class);
User user = userDao.selectUserByName("zeor");
System.out.println(user);
}
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - ==> Preparing: select * from t_users where name=zeor
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - ==> Parameters:
完整的SQL语句:select * from t_users where name=zeor
从上面两种不同的执行结果来看,我们不难得出,#{}(预编译占位符收参),它会将收到的参数当成一个字符串,在赋值到SQL语句时会自动为其添加双引号。如:select * from t_users where name="zeor";而${}(字符串拼接符收参),它会将收到的参数直接赋值给SQL语句,不会添加任何符号,如:select * from t_users where name=zeor ,这种SQL语句是错误的,要想正确执行,就必须修改SQL查询语句,让其更改为下图所示(把${name}用‘’单引号括起来
<select id="selectUserByName" resultType="com.zf.entity.User">
select * from t_users where name='${name}'
</select>
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - ==> Preparing: select * from t_users where name='zeor'
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - ==> Parameters:
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - <== Total: 1
假设,通过设计,把查询的姓名写为:tom' or '1'='1, 我们来看一下执行结果 :
<select id="selectUserByName" resultType="com.zf.entity.User">
select * from t_users where name='${name}'
</select>
@Test
public void selectUserByName() throws IOException {
UserDao userDao = MybatisUtils.getMapper(UserDao.class);
User user = userDao.selectUserByName("tom' or '1'='1");
System.out.println(user);
}
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - ==> Preparing: select * from t_users where name='tom' or '1'='1'
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - ==> Parameters:
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - <== Total: 5
从执行结果打印的日志我们可以看到,它最终执行的完整的SQL语句为:select * from t_users where name='tom' or '1'='1',它的查询条件始终为true,这样就会把表里所有的数据全都查询出来。引起严重的信息泄露事故。这种情况就是SQL注入。
最后我们再来看,如果把${}收参改为#{}收参,会是什么结果呢?
<select id="selectUserByName" resultType="com.zf.entity.User">
select * from t_users where name=#{name}
</select>
@Test
public void selectUserByName() throws IOException {
UserDao userDao = MybatisUtils.getMapper(UserDao.class);
User user = userDao.selectUserByName("tom' or '1'='1");
System.out.println(user);
}
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - ==> Preparing: select * from t_users where name=?
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - ==> Parameters: tom' or '1'='1(String)
logback [main] DEBUG com.zf.dao.UserDao.selectUserByName - <== Total: 0
最终执行的SQL语句为:select * from t_users where name=" tom' or '1'='1 "
从最后的执行SQL语句我们可以看到,他会把tom' or '1'='1整个当成一个名字,然后用“”括起来。其结果就是没有查到任何数据 ,避免了SQL注入的问题。
通过上述代码,总结如下:
1. ${}会直接把参数值赋值到SQL语句里,不添加任何的符号,这样会造成SQL注入问题。
2. #{}会把收到的参数值当成一个字符串赋值到SQL语句里,并自动添加双引号,这样不会造成SQL注入。
3. 要想避免SQL注入,尽可能不用${}收参。${}适用于SELECT * FROM t_users
ORDER BY id ${rule} 这种排序关键字的收参。