You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复Spring JDBC中JdbcTemplate.query的SQL注入SonarQube问题?

修复Spring JDBC的SQL注入问题(SonarQube告警)

我来帮你理清这个问题——你遇到的核心问题是把占位符?放在了单引号里面,导致它被当成了普通字符串,而不是预编译语句的参数占位符。咱们一步步来修复:

1. 你的错误原因

当你写date('?')时,SQL解析器会把'?'当成一个字面量字符串(也就是数据库会直接查找日期等于'?'的数据),而不是预编译的参数占位符。JdbcTemplate根本不会识别这个位置需要替换参数,自然达不到防注入的效果,还会导致查询逻辑错误。

2. 正确的占位符写法

预编译占位符?不需要加单引号,JdbcTemplate会根据你传入参数的类型(比如日期、字符串)自动处理格式转换和安全注入。你只需要把占位符直接作为SQL函数的参数即可:

StringBuilder query = new StringBuilder();
query.append("SELECT * from Events EVNT")
     .append(" WHERE EVNT.Roll IN ('5', '1') AND EVNTDemo.Roll is null ")
     .append(" AND date(EVNT.Updt) between date(?) AND CURRENT DATE - 10 DAY");

// 注意:要把StringBuilder转成String传入query方法
final Object[] args = new Object[]{getTMyDate()};
result = jdbcTemplate.query(query.toString(), args, new MyMapper());

这里的关键改动:

  • 去掉了?周围的单引号,改成date(?)
  • 显式调用query.toString(),确保传入的是完整的SQL字符串(部分JdbcTemplate版本可以直接接收StringBuilder,但显式转换更稳妥)

3. 其他修复SQL注入的方法

除了基础的?占位符,还有几种更优雅、更安全的方案:

a. 使用NamedParameterJdbcTemplate(推荐,可读性更高)

如果你的SQL有多个参数,用命名参数代替?可以避免参数顺序搞错的问题,代码可读性也更强:

// 用多行字符串写法更清晰(Java 15+支持)
String sql = """
    SELECT * from Events EVNT
    WHERE EVNT.Roll IN ('5', '1') AND EVNTDemo.Roll is null 
    AND date(EVNT.Updt) between date(:startDate) AND CURRENT DATE - 10 DAY
""";

Map<String, Object> params = new HashMap<>();
params.put("startDate", getTMyDate());

// 基于现有jdbcTemplate创建NamedParameterJdbcTemplate
NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
result = namedJdbcTemplate.query(sql, params, new MyMapper());

b. 处理动态IN子句(如果IN列表是动态的)

如果未来EVNT.Roll IN (...)里的值不是固定的'5','1',而是动态生成的,绝对不能直接拼接字符串。可以用NamedParameterJdbcTemplate直接传入列表参数:

// 假设rollValues是动态获取的列表
List<String> rollValues = Arrays.asList("5", "1");
String sql = """
    SELECT * from Events EVNT
    WHERE EVNT.Roll IN (:rolls) AND EVNTDemo.Roll is null 
    AND date(EVNT.Updt) between date(:startDate) AND CURRENT DATE - 10 DAY
""";

Map<String, Object> params = new HashMap<>();
params.put("rolls", rollValues);
params.put("startDate", getTMyDate());

NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
result = namedJdbcTemplate.query(sql, params, new MyMapper());

c. 使用Spring Data JPA(如果项目兼容)

如果你的项目已经引入了Spring Data JPA,直接写Repository方法即可,框架会自动处理预编译和防注入,完全不用手动拼接SQL:

public interface EventRepository extends JpaRepository<Event, Long> {
    @Query("SELECT evnt FROM Event evnt WHERE evnt.roll IN (:rolls) AND evnt.demoRoll IS NULL " +
           "AND FUNCTION('date', evnt.updt) BETWEEN FUNCTION('date', :startDate) AND CURRENT_DATE - 10")
    List<Event> findEventsByRollsAndDateRange(@Param("rolls") List<String> rolls, @Param("startDate") Date startDate);
}

最后再强调一遍核心原则:永远不要用字符串拼接的方式把动态值(哪怕是你自己生成的)直接插入SQL语句,所有动态值都必须通过预编译占位符传入,这是防止SQL注入的根本方法。

内容的提问来源于stack exchange,提问作者GKr297

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 14:02:52