MyBatis中#{}和${}的区别,什么是sql注入?如何防止?

本文介绍了MyBatis中#{}和${}的区别,重点讨论了它们在防止SQL注入方面的作用。#{}作为预编译占位符,能有效防止SQL注入,而${}进行字符串拼接,可能导致安全问题。建议尽量使用#{}来避免SQL注入,${}适合用于排序关键字的动态SQL构建。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先咱们先看#{}收参的代码及执行结果:

 <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}  这种排序关键字的收参。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值