#{ } 和 ${ } 的区别
#{ }:预编译处理。
${ }:字符直接替换。
预编译处理是指:MyBatis 在处理 #{ } 时,会将 SQL 中的 #{…} 替换为 ? 号,使⽤ PreparedStatement 的 set ⽅法来赋值。
直接替换:是MyBatis 在处理 ${ } 时,就是把 ${ } 直接替换成变量的值。
- 定义不同: #{ } 是预处理, ${ } 是直接替换
- 安全性不同: #{ } 没有安全问题, ${ } 有 SQL 注入问题
- 使用场景不同: #{ } 适用于所有类型的参数匹配, 但是 ${ } 只使用于数值类型和关键字
#{ } 使用场景
- 使用 #{ }
<insert id="add">
insert into userinfo(username,password,photo,state)
values(#{username},#{password},#{photo},1)
</insert>
==> Preparing: insert into userinfo(username,password,photo,state) values(?,?,?,1)
==> Parameters: zhaoliu(String), 666(String), img.png(String)
<== Updates: 1
先将 参数的位置替换为 ? 然后再把对应类型的参数填入进去.
- 使用 ${ }
<insert id="add">
insert into userinfo(username,password,photo,state)
values(${username},${password},${photo},1)
</insert>
==> Preparing:insert into userinfo(username,password,photo,state) values(zhaoliu,666,img.png,1)
==> Parameters:
直接替换上了,所以 Parameters 为空, 替换时也不加 引号, 所以会插入失败
like 查询使用 #{ } 报错
<select id="findUserByName" resultType="com.example.springboot3.model.User">
select * from userinfo where username like '%#{username}%';
</select>
相当于:
select * from userinfo where username like '%'username'%'
所以代码报错
这个是不能直接使⽤ ${ },可以考虑使⽤ mysql 的内置函数 concat() 来处理
concat('a', 'b', 'c') ==> 'abc'
实现代码如下:
<select id="findUserByName" resultType="com.example.springboot3.model.User">
select * from userinfo where username like concat('%', #{username}, '%');
</select>
==> Preparing: select * from userinfo where username like concat('%', ?, '%');
==> Parameters: zhaoliu(String)
<== Columns: id, username, password, photo, createtime, updatetime, state
<== Row: 7, zhaoliu, 666, img.png, 2023-10-09 11:29:00.0, 2023-10-09 11:29:00.0, 1
<== Total: 1
${ } 的使用场景
关键字的替换, 比如排序
<select id="getAll" resultType="com.example.springboot3.model.User">
select * from userinfo order by id ${rule}
</select>
==> Preparing: select * from userinfo order by id desc
==> Parameters:
使⽤ ${ } 可以实现排序查询,⽽使⽤ #{ } 就不能实现排序查询了,当使⽤ #{ } 查 询时,如果传递的值为 String 则会加单引号,就会导致 sql 错误。
所以:当传递的是一个 SQL 关键字时, 只能使用 ${ }
SQL 注入问题
假如我们真的使用了 ${ }, 那么因为我们知道 ${ } 是直接替换, 不会 加引号, 那么我们就会自己加上引号, 比如下面:
<select id="isLogin" resultType="com.example.springboot3.model.User">
select * from userinfo where username='${name}' and password='${pwd}'
</select>
但是, 这样会引入一个很大的安全问题: SQL 注入问题
sql 注入代码关键部分:
"' or 1='1"
调用接口进行测试:
List<User> list = userMapper.isLogin("afeagdee445", "' or 1 = '1");
if (list.size() > 0) {
System.out.println("登录成功");
}
查看结果:
==> Preparing: select * from userinfo where username='afeagdee445' and password='' or 1 = '1'
==> Parameters:
<== Columns: id, username, password, photo, createtime, updatetime, state
<== Row: 1, admin, admin, , 2021-12-06 17:10:48.0, 2021-12-06 17:10:48.0, 1
<== Row: 2, mysql, mysql, img.png, 2023-10-08 15:09:07.0, 2023-10-08 15:09:07.0, 1
<== Row: 3, mysql, mysql, img.png, 2023-10-08 15:24:24.0, 2023-10-08 15:24:24.0, 1
<== Row: 4, mysql, mysql, img.png, 2023-10-08 15:24:50.0, 2023-10-08 15:24:50.0, 1
<== Row: 5, mysql, mysql, img.png, 2023-10-08 15:25:59.0, 2023-10-08 15:25:59.0, 1
<== Row: 6, mysql, mysql, img.png, 2023-10-08 15:26:40.0, 2023-10-08 15:26:40.0, 1
<== Row: 7, zhaoliu, 666, img.png, 2023-10-09 11:29:00.0, 2023-10-09 11:29:00.0, 1
<== Total: 7
登录成功
登录成功了.
也就是说, 我们什么都不知道, 只要使用 SQL 注入就能得登录成功
由打印的 SQL 日志 我们就能看出来原因了
select * from userinfo where username='afeagdee445' and password='' or 1 = '1'
拼接出来的这个 SQL 的判断条件一定为 true, 因为最后是一个 or 1 = ‘1’, 这一定是 true 的.
但如果使用 #{ } 就不会有问题了
==> Preparing: select * from userinfo where username=? and password=?
==> Parameters: afeagdee445(String), ' or 1 = '1(String)
<== Total: 0
它这个等价于:
select * from userinfo where username='afeagdee445' and password="' or 1 = '1"
这里面单引号和双引号并不会匹配上
结论:⽤于查询的字段,尽量使⽤ #{} 预查询的⽅式。
当不得不使用 ${ } 时,在业务代码中一定要对传递的值进行校验.
好啦! 以上就是对 #{ } 和 ${ } 区别 的讲解,希望能帮到你 !
评论区欢迎指正 !