核心结论:MP 自带的
QueryWrapper/LambdaQueryWrapper条件构造器默认就是安全的,底层使用预编译参数绑定,不会 SQL 注入。 风险点只有一个:自己拼接字符串到 SQL 里(getSqlSelect、apply、${}原生 xml)。
1 ✅ 安全写法:LambdaQueryWrapper(推荐,编译期校验字段,最安全)
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public User getUserByName(String username) {
// Lambda 写法,直接引用实体类属性,不会写错字段名,底层预编译 ?
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
.eq(User::getUsername, username);
// 传入 payload: ' or '1'='1
// 底层SQL:SELECT id,username,age FROM user WHERE username = ?
return userMapper.selectOne(wrapper);
}
}
2 ✅ 安全写法:普通 QueryWrapper(参数值安全,但字段名是字符串硬编码)
public User getUserByName(String username) {
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.eq("username", username); // 值:username 使用预编译,安全
return userMapper.selectOne(wrapper);
}
⚠️ 注意:
eq("username", val)第二个参数是查询值,安全;第一个参数是数据库字段名,是字符串,这里如果字段名来自外部用户输入,会有风险! 例:eq(userInputField, val)userInputField 如果是前端传的字段名,存在注入风险!要用白名单校验。
3 ⚠️ 高危:apply 方法(自定义 SQL 片段,容易注入)
apply 支持拼接 SQL,参数必须使用占位符 {0} 传参,不要直接字符串拼接。
✅ apply 安全写法(使用占位符传参)
// 正确:参数用占位符,底层预编译
wrapper.apply("date(create_time) = {0}", "2026-09-15");
❌ apply 危险写法(字符串直接拼接,SQL 注入漏洞)
// 禁止!直接拼接字符串,payload 会被带入SQL
String dateVal = "2026-09-15' or 1=1 --";
wrapper.apply("date(create_time) = '" + dateVal + "'");
4 ⚠️ 动态排序 orderBy 场景(高频踩坑点)
MP 的 orderByAsc / orderByDesc:
参数是字段名,不是查询值;如果字段来自前端传入,必须白名单校验!
✅ 安全示例(白名单校验动态排序字段)
// 允许排序的字段白名单
private static final Set<String> SORT_WHITE_LIST = Set.of("id", "username", "age");
public List<User> listUser(String sortField, boolean isAsc) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("age", 18);
// 白名单校验
if (sortField != null && SORT_WHITE_LIST.contains(sortField)) {
if (isAsc) {
wrapper.orderByAsc(sortField);
} else {
wrapper.orderByDesc(sortField);
}
} else {
// 默认排序
wrapper.orderByDesc("id");
}
return userMapper.selectList(wrapper);
}
❌ 禁止:直接把前端传入的 sortField 丢进 orderByAsc,攻击者传
id;drop table user;--会造成风险。
5 ⚠️ 自定义 XML + MP 混合场景(和原生 MyBatis 一样规则)
✅ #{} 安全
<select id="selectUser" resultType="User">
SELECT id,username,age FROM user WHERE username = #{username}
</select>
❌ ${} 直接替换,注入风险,仅表名 / 排序字段可用,必须白名单。
6 ⚠️ 特殊方法:getSqlSelect / setSqlSelect
setSqlSelect 用来指定查询字段,字段字符串如果来自用户输入,存在注入风险。 ✅ 安全:固定字段
wrapper.setSqlSelect("id,username");
❌ 危险:用户输入字段直接拼接
// userInputCols 前端传入,风险!
wrapper.setSqlSelect(userInputCols);
7 完整测试注入 payload
// 注入测试 payload
String payload = "' or '1'='1";
User user = getUserByName(payload);
// 底层执行:WHERE username = ?,参数值就是 "' or '1'='1",不会解析成SQL语法,查不到数据
System.out.println(user);
总结 MP 防注入清单
额外加固建议
业务层增加输入正则校验,限制特殊字符;
使用数据库最小权限账号;
开启 MyBatis-Plus 的 SQL 日志,生产环境关闭;
代码审计重点扫描:
apply、orderBy、setSqlSelect、xml 中${。
本文原创作者:易君召,详见:https://www.yijunzhao.cc/about,转载请注明出处。
原文链接
欢迎访问