侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

MyBatis动态SQL中如何安全处理IN查询并防止SQL注入?

2025-12-13 / 0 评论 / 4 阅读

题目

MyBatis动态SQL中如何安全处理IN查询并防止SQL注入?

信息

  • 类型:问答
  • 难度:⭐⭐

考点

动态SQL编写,SQL注入防护,MyBatis参数处理

快速回答

安全处理IN查询的核心要点:

  • 使用<foreach>标签动态生成占位符
  • 必须使用#{item}预编译占位符而非${item}拼接
  • 对集合参数进行非空校验
  • 当集合为空时提供fallback方案(如返回空结果)
## 解析

1. 问题背景与原理说明

在MyBatis处理WHERE column IN (value1, value2)查询时,直接拼接字符串会导致:

  • SQL注入风险:使用${param}拼接用户输入
  • 语法错误:集合为空时生成IN ()非法语句
  • 性能问题:未利用预编译优势

MyBatis通过OGNL表达式解析动态SQL标签,#{}会被转换为JDBC预编译占位符(?),而${}直接文本替换。

2. 正确代码示例

<select id="selectUsersByIds" resultType="User">
  SELECT * FROM users
  WHERE id IN
  <foreach item="id" collection="idList" 
           open="(" separator="," close=")">
    #{id}  <!-- 关键:使用#{}预编译 -->
  </foreach>
  <if test="idList == null or idList.isEmpty()">
    AND 1=0  <!-- 集合为空时返回空结果 -->
  </if>
</select>

3. 最佳实践

  • 参数校验:在Mapper接口方法中验证集合非空
    List<User> selectUsersByIds(@Param("idList") List<Long> idList);
  • 防御空集合<if>标签处理空集合,避免生成非法SQL
  • 使用命名参数@Param明确指定集合名称
  • 大小限制:对IN列表长度做业务层限制(如最大1000项)

4. 常见错误

  • 错误1:使用${}拼接
    <foreach>${id}</foreach> → 导致SQL注入
  • 错误2:省略空集合处理
    未处理idList.isEmpty() → 执行时报语法错误
  • 错误3:错误嵌套引号
    IN ('<foreach>...</foreach>') → 破坏预编译结构

5. 扩展知识

  • 批量插入优化:同样原理实现安全批量插入
    <insert id="batchInsert">
      INSERT INTO table(field)
      VALUES
      <foreach item="item" collection="list" separator=",">
        (#{item.value})
      </foreach>
    </insert>
  • 动态SQL替代方案
    • 注解方式使用@SelectProvider动态生成SQL
    • MyBatis-Plus的QueryWrapper.in()方法
  • 预编译原理:数据库先接收带占位符的SQL模板,再接收参数值执行,从根本上隔离指令与数据