正确使用 preparedstatement 防 sql 注入的关键是所有用户输入必须通过 setxxx() 绑定,确保 sql 结构与数据彻底分离;表名、列名等结构化内容须白名单校验,禁止字符串拼接,且需复用实例以发挥性能优势。

用对 PreparedStatement 才能真正防住 SQL 注入。关键不在“用了没”,而在“怎么用”——所有用户输入必须走 setXxx() 绑定,SQL 结构和数据必须彻底分离。
所有参数必须走 setXxx() 绑定
占位符 ? 只代表一个值位置,数据库在预编译阶段就固定语法结构,后续传入的值仅作为纯数据,不参与任何解析。哪怕传入 "admin' OR '1'='1",它也只会被当做一个完整字符串去匹配。
- 数字类型必须用
setInt()、setLong(),不能用setString()强转 - 时间字段优先用
setTimestamp()或setObject(..., LocalDateTime.class),避免格式错乱 - 空值必须调用
setNull(index, Types.INTEGER)等明确类型的方法,不能向setInt()传null
表名、列名、排序字段等结构内容严禁拼接
? 占位符只支持值,不支持 SQL 语法结构。一旦拼接用户可控内容(如表名、ORDER BY 字段、ASC/DESC),预编译立即失效。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 错误写法:
"SELECT * FROM " + tableName + " WHERE id = ?" - 安全做法:对动态结构做白名单校验,例如
if ("name".equals(sortField)) sql += " ORDER BY name"; - 分页参数
limit、offset必须校验为非负整数,不能直接拼进 SQL
IN 子句与批量操作要规范处理
单个 ? 无法代替多个值,WHERE id IN (?) 只能匹配一个 ID。手动拼 "?, ?, ?" 是高危行为。
- 限制最大数量(如 ≤ 100),再用循环
addBatch()批量执行 - 避免在 SQL 字符串里用
String.join()或正则生成占位符序列 - 高频查询建议复用 PreparedStatement 实例,而非每次 new 一个新对象
字符集与驱动配置必须统一且严格
编码错位可能让转义失效,导致绕过防护。比如 URL 编码的 %27 被后端主动 URLDecoder.decode() 还原成单引号,再进 setString() 就等于自毁防线。
- JDBC URL 显式指定:
useUnicode=true&characterEncoding=UTF-8 - Oracle 驱动需禁用模拟预处理:
&oracle.jdbc.autoCommitSpecCompliant=false - 用户原始输入(未经任何 decode)直传
setString(),不清洗、不转义、不替换
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










