一、什么是sql注入
public class TestSql {
public static void main(String[] args) {
Scanner inScanner = new Scanner(System.in);
System.out.println("请输入用户名");
String username = inScanner.nextLine();
System.out.println("请输入密码");
String password = inScanner.nextLine();
String sql = "select * from user where username = '"+username+"' and password = '"+password+"'";
search(sql);
}
public static void search(String sql) {
try {
Class.forName("com.mysql.jdbc.Driver"); // 1.加载驱动
//2.建立连接
Connection connection = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/jiudian?useUnicode=true&characterEncoding=utf-8", "root", "2020");
//3.创建执行的SQL语句
Statement statement = (Statement) connection.createStatement();
//4.执行sql语句
ResultSet re = (ResultSet) statement.executeQuery(sql);
//5.处理结果
if(re.next()) {
System.out.println("查询成功...");
}else {
System.out.println("查询失败...");
}
//6.释放资源
if(re!=null) {
re.close();
}
if(statement !=null) {
statement.close();
}
if (connection !=null) {
connection.close();
}
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("找不到驱动类,加载失败");
e.printStackTrace();
}
}
}
以上的程序我们经过测试没什么问题。
但是当输入如下数据的时候,我们会发现一个问题
那么为什么会产生这个现象呢?
我们将拼接好的sql语句拿出来
select * from user where username =’ 111’ and password = ‘1’ or ‘1’=‘1’;
这种通过传参就能改变SQL语句原本规则的操作就是SQL注入,这个在实际生产中当然是危险的,攻击者可以把SQL命令插入到Web表单的输入域或页面请求的查询字符串中,欺骗服务器执行恶意的SQL命令。
以上的情况就属于sql注入攻击。
二、为什么会产生sql注入问题呢?
我们可以把sql语句的执行流程大致分为:
本地sql语句拼接 ------》发送sql语句给DBMS----->DBMS进行sql编译。
造成sql注入的原因在于我们在本地拼接了一条“有安全隐患的”sql语句。之后我们将拼接好的sql语句发送给DBMS,DBMS将“有安全隐患”的sql语句进行了编译执行。
这里的重点是:用户的信息参与到了编译过程,而这个信息出现了问题
三、如何解决sql注入问题呢?
其实解决方法很简单:只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。
要想让用户的信息不参与SQL语句的编译,那么就必须使用java.sql.PreparedStatement接口。
PreparedStatement是属于预编译的数据库操作对象,其原理是:预先对sql语句的框架进行编译,然后再给给sql赋值。预编译完成之后我们的DBMS只需要执行我们的sql语句,没必要再次编译。
使用PreparedStatement需要注意的几点
①:占位符
String sql = "select * from user where username = ? and password = ?";
我们拼写好的sql语句不在使用直接拼接赋值的方式,而是采用 ? 占位符进行代替
②:创建方式
PreparedStatement statement = (PreparedStatement) connection.prepareStatement(sql);
在这个地方我们需要提前编译sql语句
③:占位符赋值
statement.setString(1, usernanme);
statement.setString(2, password);
总体代码如下
public class TestSql {
public static void main(String[] args) {
Scanner inScanner = new Scanner(System.in);
System.out.println("请输入用户名");
String username = inScanner.nextLine();
System.out.println("请输入密码");
String password = inScanner.nextLine();
String sql = "select * from user where username = ? and password = ?";
search(sql,username,password);
}
public static void search(String sql,String usernanme,String password) {
try {
Class.forName("com.mysql.jdbc.Driver"); // 1.加载驱动
//2.建立连接
Connection connection = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/jiudian?useUnicode=true&characterEncoding=utf-8", "root", "2020");
//3.创建执行的SQL语句
PreparedStatement statement = (PreparedStatement) connection.prepareStatement(sql);
// 给占位符赋值
statement.setString(1, usernanme);
statement.setString(2, password);
//4.执行sql语句
ResultSet re = (ResultSet) statement.executeQuery();
//5.处理结果
if(re.next()) {
System.out.println("查询成功...");
}else {
System.out.println("查询失败...");
}
//6.释放资源
if(re!=null) {
re.close();
}
if(statement !=null) {
statement.close();
}
if (connection !=null) {
connection.close();
}
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("找不到驱动类,加载失败");
e.printStackTrace();
}
}
}