1. #{} 与 ${} 的 SQL 注入与预编译
请说明 MyBatis 中 #{} 与 ${} 的区别,以及它们与 SQL 注入、预编译的关系?
- #{} 使用 PreparedStatement 参数占位符
- ${} 直接字符串拼接
- 防注入与灵活性的权衡
MyBatis 中 #{} 会解析为 PreparedStatement 的占位符(?),参数值由 JDBC 预编译绑定,可防止 SQL 注入;${} 直接进行字符串替换,把值拼进 SQL 文本,存在 SQL 注入风险,只在表名、列名、排序字段等无法参数化的场景使用并需严格校验。预编译(PreparedStatement)将 SQL 结构与参数分离,参数作为数据传递,恶意输入无法改变 SQL 结构,因此 #{} 是安全的。${} 还会使 SQL 无法被缓存预编译,影响性能。
安全优先原则是默认用 #{},${} 仅限必须动态指定标识符(表名/列名/排序/分页)且经过白名单校验。理解预编译机制是防注入的核心。
-- 安全:参数化
SELECT * FROM t_user WHERE name = #{name}
-- 危险:字符串拼接,name 可注入 SQL
SELECT * FROM t_user WHERE name = ${name}