两者本质区别
${params.id}:MyBatis 直接字符串替换,把值原样拼进 SQL。相当于字符串拼接。#{params.id}:MyBatis 用 PreparedStatement 预编译占位符?,值由驱动单独传参,和 SQL 语句分离。
数字类型在数据库里会被自动转换,所以 #{} 对数字完全适用——数字和字符串都应该用 #{}。
为什么 ${} 危险(即使是数字列)
以你原来 where id =${params.id} 为例:
-- 正常:id = 100 delete from sys_app_permission where id =100-- 攻击:id 传 "1 or 1=1"(虽然是 GET 参数,攻击者可以直接拼) delete from sys_app_permission where id =1 or 1=1 -- 全表删光-- 攻击:id 传 "1; drop table xxx" delete from sys_app_permission where id =1; drop table xxx
攻击者传的是字符串,不是数字。${} 不知道、也不关心你“本意”是数字,它只负责拼接,所以注入就发生了。
改成 #{} 后:
delete from sys_app_permission where id = ? -- 参数 '1 or 1=1' 作为整体传给数据库
数据库把 '1 or 1=1' 当成 id 的值去匹配(数字列会转换失败报错,而不是执行注入),注入被彻底堵死。
那什么时候必须用 ${}?
只有 表名/字段名这类标识符才必须用 ${},因为 #{} 会给标识符加引号变成字符串值,SQL 不合法。
