【Java踩坑笔记】41_MyBatis的井号和$用错就是SQL注入
41 | MyBatis 的 #{} 和 ${} 用错就是 SQL 注入摘要#{}是参数占位符预编译安全${}是字符串替换直接拼 SQL有注入风险。动态表名、LIKE 查询的正确写法常踩坑。一、问题现象!-- ❌ 危险用 ${} 拼 WHERE 条件 --selectidfindUserresultTypeUserSELECT * FROM user WHERE name ${name}/select调用findUser(Alice OR 11)→ 生成 SQLSELECT*FROMuserWHEREnameAliceOR11-- 全表泄露二、#{} vs ${} 本质区别语法处理方式是否预编译是否有注入风险#{name}?占位符PreparedStatement.setObject()✅ 是❌ 无${name}直接字符串替换拼接❌ 否✅有#{}生成的 SQLSELECT*FROMuserWHEREname?-- 预编译然后ps.setObject(1, Alice)→ 安全。${}生成的 SQLSELECT*FROMuserWHEREnameAlice-- 直接拼接如果name Alice OR 11就注入了。三、踩坑现场场景 1LIKE 查询用了 ${}!-- ❌ 危险 --selectidsearchresultTypeUserSELECT * FROM user WHERE name LIKE %${keyword}%/select修复用CONCATMySQL或数据库函数!-- ✅ 正确用 #{} CONCAT --selectidsearchresultTypeUserSELECT * FROM user WHERE name LIKE CONCAT(%, #{keyword}, %)/select场景 2动态表名必须用${}但要白名单!-- ⚠️ 动态表名只能用 ${}但要校验 --selectidqueryresultTypemapSELECT * FROM ${tableName} WHERE id #{id}/select修复Java 代码里做白名单校验// ✅ 正确白名单校验publicListMapString,Objectquery(StringtableName){if(!ALLOWED_TABLES.contains(tableName)){thrownewIllegalArgumentException(非法表名);}returnmapper.query(tableName);}四、正确写法4.1 能用 #{} 就不用 ${}!-- ✅ 正确 --selectidfindUserresultTypeUserSELECT * FROM user WHERE name #{name} AND age #{minAge}/select4.2 LIKE 查询用 CONCATMySQL或bind通用!-- ✅ MySQLCONCAT --selectidsearchresultTypeUserSELECT * FROM user WHERE name LIKE CONCAT(%, #{keyword}, %)/select!-- ✅ 通用bind 标签 --selectidsearchresultTypeUserbindnamepatternvalue% keyword %/SELECT * FROM user WHERE name LIKE #{pattern}/select4.3 动态表名白名单校验// ✅ 正确Java 代码里校验不信任前端传入的表名privatestaticfinalSetStringALLOWEDSet.of(user,order,product);publicvoidquery(StringtableName){if(!ALLOWED.contains(tableName))throw...;mapper.dynamicQuery(tableName);}五、最佳实践✅ MyBatis SQL 编写的 3 条铁律能用#{}就不用${}LIKE 查询用CONCAT(#{}, ...)不要用${}动态表名用${}时必须在 Java 代码里做白名单校验 IDEA 插件安装MyBatisX插件语法高亮#{}/${}警告用${}时高亮提示点击跳转 XML ↔ Mapper 接口六、小结#{}→?占位符预编译安全优先用${}→ 字符串直接替换有 SQL 注入风险只在动态表名、动态排序字段时用动态表名用${}时必须在 Java 代码里做白名单校验LIKE 查询用CONCAT(%, #{keyword}, %)不要用${keyword}安装MyBatisX插件IDEA 里直接提示${}风险下一篇预告数据库连接池满了超时先看看你的事务有没有关—— 连接泄漏最常见的坑事务没提交连接不释放。更多资料一线大厂Java面试题合集