主要介绍Statement和PrepardStatement的区别
1.编码简单
简单介绍一下如何简单化编码
st:
String sql="insert into student (stuno,stuname)values('"+id+"','"+name+"');
st.executeUpdate(sql);
pst:
String sql="insert into student(stuno,stuname)"values(?,?)";
pst=Connection.prepareStatement(sql);//进行预编译
pst.setString(1,id);
pst.setString(2,name);
pst.executeUpdate();
虽然看起来却pst好像代码更复杂但是留意的小伙伴会发现其实变得更简单它在赋值的时候避免了字符串的拼接
2.提高性能
还是上面的代码,如果重复操作插入此数据100次st每次都需要st.executeUpdate(sql);才能执行后面的操作,但是pst只需要执行一次pst=Connection.prepareStatement(sql);//进行预编译之后就可以执行后面的操作!(当然有更好的处理办法就是批处理方法这里只是用来举列子!
3.防止SQL注入
所谓SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令,比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击.
当应用程序使用输入内容来构造动态sql语句以访问数据库时,会发生sql注入攻击。如果代码使用存储过程,而这些存储过程作为包含未筛选的用户输入的 字符串来传递,也会发生sql注入。sql注入可能导致攻击者使用应用程序登陆在数据库中执行命令。如果应用程序使用特权过高的帐户连接到数据库,这种问 题会变得很严重。在某些表单中,用户输入的内容直接用来构造动态sql命令,或者作为存储过程的输入参数,这些表单特别容易受到sql注入的攻击。而许多 网站程序在编写时,没有对用户输入的合法性进行判断或者程序中本身的变量处理不当,使应用程序存在安全隐患。这样,用户就可以提交一段数据库查询的代码, 根据程序返回的结果,获得一些敏感的信息或者控制整个服务器,于是sql注入就发生了
举个例子:
写一个st访问数据库的程序
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
public class sql_injection {
// 声明Connection对象
public static Connection con;
// 驱动程序名
String driver = "com.mysql.cj.jdbc.Driver";
// URL指向要访问的数据库名studata
String url = "jdbc:mysql://localhost:3306/studata?serverTimezone=GMT";// 加上解决时差问题
// MySQL配置时的用户名
String user = "root";
// MySQL配置时的密码
String password = "123qwe";
public void query() {
try {
// 加载驱动程序
Class.forName(driver);
// 1.getConnection()方法,连接MySQL数据库!!
con = DriverManager.getConnection(url, user, password);
if (!con.isClosed())
System.out.println("Succeeded connecting to the Database!");
// 2.创建statement类对象,用来执行SQL语句!!
Statement statement = con.createStatement();
Scanner sc=new Scanner(System.in);
System.out.println("请输入用户名:");
String name=sc.nextLine();
System.out.println("请输入密码:");
String pwd=sc.nextLine();
// 要执行的SQL语句
String sql = "select count(*) from login where user='"+name+"'and pawd='"+pwd+"'";
// 3.ResultSet类,用来存放获取的结果集!!
ResultSet rs = statement.executeQuery(sql);
int count=-1;
if (rs.next()) {
count=rs.getInt(1);
}
if(count>0) {
System.out.println("登陆成功");
}
else {
System.out.println("登陆失败");
}
rs.close();
con.close();
} catch (ClassNotFoundException e) {
// 数据库驱动类异常处理
System.out.println("Sorry,can`t find the Driver!");
e.printStackTrace();
} catch (SQLException e) {
// 数据库连接失败异常处理
e.printStackTrace();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
}
}
public static void main(String[] args) {
sql_injection db=new sql_injection();
db.query();
}
}
先输入正确的用户名admin和密码123456登陆必然成功
再输错误一个错误的admin 12
演示一下错误的sql注入问题
sql注入再输入' '这种字符时statement无法识别会出现意想不到的错误,会出现异常也就是当你数据库中的用户名含有“ ' ”导致无法验证也就无法登陆
还有一种更可怕的登陆情况比如一个可能也是编程人员当他输入用户名“ 任意值 ' or 1==1 -- 密码:任意值
也能登陆成功。
分析原因
原本的sql语句:
sql = select count(*) from login where user='"+name+"' and pawd='"+pwd+"'
把用户名粘进去可得到:
sql = select count(*) from login where user='任意值 ' or 1==1 -- ' and pawd='"+pwd+"' (注释掉)
虽然任意值是假 但是or 1==1就是真,接着--数据库中的注释语句注释掉了后面的所有值所以sql语句变成下面这样
sql=select count(*) from login where user=1==1(真)
相当于
sql=select count(*) from login 必然能执行所以肯定能验证
而PreparedStatement就可以避免这个问题
package jdbcMysql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
public class sql_injection {
// 声明Connection对象
public static Connection con;
// 驱动程序名
String driver = "com.mysql.cj.jdbc.Driver";
// URL指向要访问的数据库名studata
String url = "jdbc:mysql://localhost:3306/studata?serverTimezone=GMT";// 加上解决时差问题
// MySQL配置时的用户名
String user = "root";
// MySQL配置时的密码
String password = "123qwe";
@SuppressWarnings("resource")
public <PrepareStatement> void query() {
try {
// 加载驱动程序
Class.forName(driver);
// 1.getConnection()方法,连接MySQL数据库!!
con = DriverManager.getConnection(url, user, password);
if (!con.isClosed())
System.out.println("Succeeded connecting to the Database!");
// 2.创建statement类对象,用来执行SQL语句!!
Scanner sc=new Scanner(System.in);
System.out.println("请输入用户名:");
String name=sc.nextLine();
System.out.println("请输入密码:");
String pwd=sc.nextLine();
// 要执行的SQL语句
String sql = "select count(*) from login where user=? and pawd=?";
// 3.ResultSet类,用来存放获取的结果集
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, name);
pst.setString(2, pwd);
ResultSet rs = pst.executeQuery();
int count=-1;
if (rs.next()) {
count=rs.getInt(1);
}
if(count>0) {
System.out.println("登陆成功");
}
else {
System.out.println("登陆失败");
}
rs.close();
con.close();
} catch (ClassNotFoundException e) {
// 数据库驱动类异常处理
System.out.println("Sorry,can`t find the Driver!");
e.printStackTrace();
} catch (SQLException e) {
// 数据库连接失败异常处理
e.printStackTrace();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
}
}
public static void main(String[] args) {
sql_injection db=new sql_injection();
db.query();
}
}
就不会像Statement出现异常
分析
String sql = select count(*) from login where user=? and pawd=? 避免了字符串的拼接
String sql =select count(*) from login where user='"+name+"'and pawd='"+pwd+";有字符串的拼接可能导致字符串中的单引号与语句中的单引号重复,就会出现不可预知的错误!