mybatis-plus 3.5 的 wrapper 本身不防注入,安全取决于使用方式:内置方法(eq/like等)通过 preparedstatement 参数绑定天然安全;apply、动态字段名、排序等结构化操作必须白名单校验。

MyBatis-Plus 3.5 的 Wrapper 本身不防注入,安全与否完全取决于你怎么用——只要不用字符串拼接、不把用户输入当字段名或 SQL 结构用,内置方法(eq、like、in 等)天然安全;但一旦误用 apply 或动态拼字段名,风险立刻拉满。
eq/like 等内置方法为什么默认安全
它们底层走 JDBC PreparedStatement 参数绑定,和 MyBatis 的 #{} 本质一致:参数值不参与 SQL 解析,只作为数据传入。
-
queryWrapper.eq("username", userInput)生成的是WHERE username = ?,即使userInput是"admin' OR '1'='1",也只会查一个用户名叫这个字符串的记录 -
like方法本身安全,但别自己拼"%" + keyword + "%"——尤其当keyword含%或_且未配escape规则时,可能破坏语义。推荐写法:queryWrapper.like("name", keyword),让框架统一处理通配符与转义 - 所有内置条件方法(
gt、between、in、isNotNull)都遵循该机制,无需额外加SqlUtils.sqlInject()或手动转义
LambdaQueryWrapper 比 QueryWrapper 更可靠在哪
关键不在 Wrapper 类型,而在字段引用方式:用 User::getUsername 替代 "username" 字符串。
-
QueryWrapper的字段名是纯字符串,若来自前端传参或配置项(比如sortField),就可能被篡改;而LambdaQueryWrapper的User::getStatus在编译期锁定字段路径,运行时通过反射提取字段名,不经过字符串解析,天然绕过字段名拼错或结构注入风险 - 错误示范:
wrapper.orderByAsc(sortParam),若sortParam是"update_time; DROP TABLE user",会直接拼进 SQL - 安全做法:白名单校验后映射到方法引用,如
if ("price".equals(sortField)) wrapper.orderByAsc(User::getPrice)
apply 方法怎么用才不踩坑
apply 是 Wrapper 里唯一能主动打开注入通道的地方,它不关心你前面用了多少个 User::getXXX,只看你传进去的 SQL 片段干不干净。
- ❌ 危险:
wrapper.apply("status = '" + statusValue + "'")—— 直接拼接,statusValue是"1'; TRUNCATE user; --"就完蛋 - ✅ 安全:
wrapper.apply("status = {0}", statusValue)——{0}触发参数绑定,等价于? - ⚠️ 注意:
{0}只对值生效,不能用于字段名、表名、排序方向等结构部分(如ORDER BY {0}会报错或被忽略) - 真要动态结构?必须白名单校验后再映射到方法引用,或用
SqlInjectionUtils.check()做兜底(该工具类在 2026 年 6 月 16 日起正式纳入官方安全扩展包)
动态字段名(排序/分组/SELECT 列)必须白名单校验
Wrapper 不处理结构动态化,orderByAsc(sortField) 中的 sortField 是直接拼进 SQL 的,不受参数绑定保护。
- 前端传来的
sort=price; DROP TABLE user会被原样塞进 SQL,变成ORDER BY price; DROP TABLE user - 必须显式校验:
if (!List.of("price", "create_time", "sales").contains(sortField)) { throw new IllegalArgumentException(); } - 更稳妥的做法是结合 LambdaQueryWrapper 和 switch/case 映射:
switch (sortField) { case "price": lambdaWrapper.orderByAsc(User::getPrice); break; ... } - 像
groupBy、动态SELECT列、HAVING条件等同理,没有“自动防注入”,只有“强制白名单”
最易被忽略的点是:以为用了 LambdaQueryWrapper 就万事大吉,结果在同一个 wrapper 里混用 apply("ORDER BY " + sortField),等于前门锁死、后窗大开。结构动态化永远需要独立校验,和参数绑定无关。











