文章目录
一、引出问题
statement在执行SQL语句之前,需要我们自己完成SQL语句的拼接
String sql = "select * from employee where account = '" + account + "' and password = '" + password + "';";
1、只能拼接字符串类型,如果你数据库有一个字符串数组,它是拼接不了的。
2、如果字段很多,拼接也很麻烦。
3、可能发生注入攻击。动态值充当了SQL语句结构,影响了原有的查询结果!
例如:or '1' = '1'
是永远成立的,此时就产生注入攻击了。
因此statement只适合执行静态的SQL语句,即没有任何动态条件值的SQL语句,如果有多个条件值,它会发生注入攻击。
而PreparedStatement叫做预编译,它会提前知道你的SQL结构,然后再给对应的结构动态赋值,不会让它充当SQL的部分,防止了注入攻击。
二、PreparedStatement和Statement步骤对比
PreparedStatement是Statement的一个子接口

statement
1.创建statement
2.拼接SQL语句
3.发送SQL语句,并且获取返回结果
preparedstatement
1.编写SQL语句结果 不包含动态值部分的语句,动态值部分使用占位符 ? 替代
PS:? 只能替代动态值
2.创建preparedstatement,并且传入SQL语句
3.动态值 占位符 赋值 ? 单独赋值即可
4.发送SQL语句即可,并获取返回结果
PS:statement是在发送SQL语句的时候传入的sql,而preparedstatement是在创建预编译statement的时候就发送了sql语句!
正是因为提前告知了SQL语句的结构,如果当外人任然使用动态值充当了SQL语句结构,此时就会直接执行失败。
三、使用PreparedStatement解决
package com.atguigu.api.PreparedStatement;
import java.sql.*;
import java.util.Scanner;
/**
* TODO:防止注入攻击 | 演示ps的使用流程
*/
public class PSUserLoginPart {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入id");
int id = scanner.nextInt();
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/itcast", "root", "123456");
//3.编写SQL语句结果
String sql = "select * from employee where id = ?;";
//4.创建预编译statement并且设置SQL语句结果
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//5.单独占位符进行赋值
/**
* 参数1:index,占位符的位置,从左向右数,从1开始
* 参数2:object,占位符的值,可以设置任何类型的数据,避免了我们凭借和类型更加丰富
*/
//其实是可以写getInt的,但建议统一写Object,因为这样就不需要考虑了
preparedStatement.setObject(1, id);
//如果有多个 ? 占位符的话,需要使用多个preparedStatement.setObject
//6.发送SQL语句,并返回返回结果!
// 此时就不需要传sql了,因为它已经知道语句动态值了!
ResultSet resultSet = preparedStatement.executeQuery();
//7.结果集解析
while(resultSet.next()) {
int id1 = resultSet.getInt("id");
String salary = resultSet.getString("salary");
System.out.println(id1 + "--" + salary);
}
//8.关闭资源
connection.close();
preparedStatement.close();
resultSet.close();
}
}
四、为啥PreparedStatement能解决注入攻击?
Statement
-
SQL语句 + 动态值
- 拼接完整SQL
- 此位置可能发生注入攻击!statement也无法识别!
-
Statement
- 执行拼接好的SQL语句
- 返回结果
PreparedStatement
-
SQL语句(使用占位符) + 动态值
- PreparedStatement
- 占位符赋值
- 此处动态值赋值,PreparedStatement已经知道语句结构!没法篡改!
-
PreparedStatement
- 执行完整的SQL语句
- 返回结果