java中preparedstatement为什么可以防止sql注入

PreparedStatement防止SQL注入。
动态SQL要验证输入。
Statement易受攻击。
用预编译语句提升安全。

preparedstatement的使用!!

昨天调试接口,一个报错挺有意思。
用户传了个奇怪的名字,系统直接崩溃了,查日志一看,SQL语句里全是乱码,典型的SQL注入。
等等,这不就是书上说的那个吗?
SQL注入就是用户往输入框里偷偷塞了SQL代码,数据库傻乎乎就当真去执行了。
比如用户输入' OR '1 '='1 ,本来该查名字的语句就变成SELECT FROM users WHERE name='' OR '1 '='1 ',结果全表查出来了。

解决方法其实很简单,用PreparedStatement。
这玩意儿像块砖,你往里塞数据,它不会把数据当代码执行。
比如Java里这么写: java String sql = "SELECT FROM users WHERE name = ?"; PreparedStatement stmt = connection.prepareStatement(sql); stmt.setString(1 , userInput); // 塞数据 ResultSet rs = stmt.executeQuery();
它内部会先编译一次SQL语句,变成个模板,之后每次执行只需要传参数,编译优化那步就省了。

这编译过程分硬解析和软解析。
硬解析每次都得重头到尾跑一遍,软解析会先看看缓存里有没有现成的计划。
PreparedStatement用的是软解析,所以效率高。

批量操作更爽,一行代码插五条数据: java String sql = "INSERT INTO users (name, age) VALUES (?, ?)"; PreparedStatement stmt = connection.prepareStatement(sql); for (int i = 0; i < 5>事务处理也简单,开始标记、提交、回滚: java connection.setAutoCommit(false); stmt.executeUpdate("DELETE FROM users WHERE id = 1 "); connection.commit(); // 出错就rollback
最麻烦的是动态SQL,比如用户可以选查年龄大于2 0的,或者按名字搜: java String sql = "SELECT FROM users WHERE age > ?"; // 或 WHERE name LIKE ? PreparedStatement stmt = connection.prepareStatement(sql); if (ageCondition) { stmt.setInt(1 , age); } else { stmt.setString(1 , "%" + name + "%"); }
这东西用多了,突然发现Java的JDBC比Node.js的MySQL库写起来还顺手。
不过Node那边也有库能自动转义,所以...
数据库服务端其实也能防注入,比如MySQL 8 .0有个PREPARE语句,但好像不是所有系统都支持。
客户端防得严,服务端就少操心。

分层设计也是这么回事。
以前写单体应用,几百行代码混在一起,改个表结构能改一天。
现在分层后,数据层就管数据库交互,业务层管逻辑,改个SQL语句直接在数据层改,其他层不管。

比如查部门表: java // 数据层 public List getDepartments(Connection conn) { String sql = "SELECT FROM departments"; PreparedStatement stmt = conn.prepareStatement(sql); return fetchResults(stmt); }
// 业务层 public List getActiveDepartments(Connection conn) { List all = getDepartments(conn); return all.stream().filter(d -> d.isActive()).collect(Collectors.toList()); }
现在业务逻辑能抽成独立服务,部署的时候还不用重启整个应用。
不过分层多了,跨层调用又得加不少RPC,这...
突然发现像LIKE这种模糊查询用PreparedStatement会慢,因为参数是占位符,优化器没法提前统计。
得写成WHERE name LIKE CONCAT('%', ?, '%')这种才行。

这SQL优化又绕回数据库引擎了,InnoDB和MyISAM的索引实现都不同...