数据库查找(漏洞 bug)小案例:
public class Test01 {
public static void main(String[] args) {
Connection conn=null;
//建立连接
try {
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");
//创建Statement
Statement stmt=conn.createStatement();
String name="name' or";
String pwd=" != '";
//声明sql
String sql="SELECT * FROM USER WHERE UNAME = '"+name+"' AND PWD = '"+pwd+"'";
System.out.println(sql);
//结果集
ResultSet rs=stmt.executeQuery(sql);
boolean flag=false;
while(rs.next()){
flag=true;
}
System.out.println(flag);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
打印结果为:
SELECT * FROM USER WHERE UNAME = 'name' or' AND PWD = ' != ''
true
分析结果:
以上程序模拟的是用户进行登陆,程序查找数据库返回结果。
通过拼装sql语句,可以查询到true,这明显是一个bug。那么如何进行解决呢?
对上一个案例问题进行解决+新的知识点(PreparedStatement以及事务的提交)实例:
public class Test02 {
public static void main(String[] args) {
test();
test2();
}
public static void test2(){
Connection conn=null;
PreparedStatement pstmt=null;//使用PreparedStatement可以防止sql注入
try {
//创建连接 rewriteBatchedStatements=true:设置批处理
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/test?"
+ "rewriteBatchedStatements=true","root","123456");
User u=new User("zhangsan","123");
//改变提交的方式,改为手动提交
conn.setAutoCommit(false);
//sql语句
String sql="INSERT INTO USER (UNAME,PWD) VALUES (?,?)";
//创建Statement
pstmt=conn.prepareStatement(sql);
long start=System.currentTimeMillis();
for(int i=0;i<100;i++){
//设置值
pstmt.setString(1, u.getName());
pstmt.setString(2, u.getPwd());
//添加到批处理中
pstmt.addBatch();
}
//执行批处理
pstmt.executeBatch();
//手动提交事务
conn.commit();
long end=System.currentTimeMillis();
System.out.println("使用PreparedStatement使用时间:"+(end-start));
} catch (SQLException e) {
e.printStackTrace();
//如果发生异常,则需要进行回滚
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally{
try {
if (pstmt != null) {
pstmt.close();
}
if(conn!=null){
conn.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
public static void test(){
Connection conn=null;
Statement stmt=null;//使用PreparedStatementf
try {
//创建连接
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");
stmt=conn.createStatement();
long start=System.currentTimeMillis();
for(int i=0;i<100;i++){
User u=new User("zhangsan","123");
//sql语句
String sql="INSERT INTO USER (UNAME,PWD) VALUES ('"+u.getName()+i+"','"+u.getPwd()+"')";
//创建Statement
stmt.executeUpdate(sql);
}
long end=System.currentTimeMillis();
System.out.println("使用Statement使用时间:"+(end-start));
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
if (stmt != null) {
stmt.close();
}
if(conn!=null){
conn.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
class User{
private int id;
private String name;
private String pwd;
public User(int id, String name, String pwd) {
super();
this.id = id;
this.name = name;
this.pwd = pwd;
}
public User(String name, String pwd) {
super();
this.name = name;
this.pwd = pwd;
}
public User() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", pwd=" + pwd + "]";
}
}
打印结果为:
使用Statement使用时间:5758
使用PreparedStatement使用时间:116
程序分析:
1、PreparedStatement :可以解决第一个sql注入的问题,PreparedStatement采用占位符,数据库内部进行sql的拼接。
2、PreparedStatement可以进行处理操作。
3、jabc默认是自动提交事务conncetion.autoCommite(true);,我们可以设置不进行自动提交事务,等所有操作处理完成之后 一起进行提交。
4、手动提交: conncetion.autoCommite(false);
5、提交:connection.commite() 回滚:connection.rollback();
6、对于批处理,PreparedStatement的处理效率比Statement的效率要高。