所有用户可控输入必须用#{}绑定,禁用${}拼接;#{}触发preparedstatement预编译,确保参数作为纯数据处理;like模糊查询需用concat或java层拼接%;动态字段须白名单校验;in查询须用标签逐项#{}绑定。

关键就一条:所有用户可控的输入,必须用 #{} 绑定,绝不用 ${} 拼接。MyBatis 本身不自动防注入,安全取决于你怎么写。
用 #{} 绑定所有变量值
这是最核心、最有效的防护手段。#{} 会触发 JDBC PreparedStatement 预编译机制,把参数当作纯数据传入,数据库不会把它当 SQL 语法解析。
- 正确写法:
<select id="getUser" resulttype="User">SELECT * FROM user WHERE username = #{username}</select> - 错误写法:
WHERE username = '${username}'或WHERE username = ${username}—— 这两种都会直接拼接字符串,输入admin' OR 1=1 --就能绕过验证 - 即使参数是数字、日期或布尔值,也统一用 #{};MyBatis 会自动适配类型,不需要手动加引号或转换
like 模糊查询的安全写法
不能写成 LIKE '%${username}%',也不能写成 LIKE '%#{username}%'(MyBatis 不支持在 #{} 外直接加 % 符号)。
Java Linux版下载入口,提供 Oracle JDK 26.0.2 官方 Linux 安装包、Java 环境配置、JDBC 数据库连接和 Java 服务端开发相关信息。
- 推荐方式一(数据库函数):
WHERE username LIKE CONCAT('%', #{username}, '%')—— 利用 MySQL 的 CONCAT,安全且跨库兼容性好 - 推荐方式二(Java 层拼接):在 Service 或 Mapper 接口传参前,把 % 加进去,如
mapper.findByUsernameLike("%" + keyword + "%"),XML 中仍用#{username} - 避免使用 ${} 拼接通配符,哪怕做了 trim 或转义,依然不可靠
动态字段(order by、表名、列名)必须白名单校验
这些无法用 #{},因为它们属于 SQL 结构的一部分,不是数据值。必须在 Java 层严格限制可选范围。
- 例如排序字段,定义合法字段列表:
private static final Set<string> ALLOWED_SORT_FIELDS = Set.of("username", "create_time", "email");</string> - 接收前端传来的 sort 字段后,先检查是否在白名单中,不在就抛异常或默认值
- XML 中再用 ${sortField},此时已确保内容可信;切勿直接将用户输入原样代入 ${}
- 同理适用于动态表名、GROUP BY 字段等场景
in 查询多个值要用 标签
不能写成 WHERE id IN (${ids}),也不能用 WHERE id IN (#{ids})(后者会把整个集合当一个参数,报错)。
- 正确写法:
WHERE id IN <foreach collection="idList" item="id" open="(" separator="," close=")">#{id}</foreach> - collection 参数名要与方法入参一致(如 List
idList),item 是循环变量名 - foreach 内部每个 #{id} 都走预编译,完全隔离注入风险
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










