java prestatement_java中PreparedStatement和Statement详细讲解

大家都知道PreparedStatement对象可以防止sql注入,而Statement不能防止sql注入,那么大家知道为什么PreparedStatement对象可以防止sql注入,接下来看我的案例大家就会明白了!

我用的是mysql数据库,以admin表为例子,如下图:

最后面有具体的java代码和sql代码案例

7759cdbc660ec148a84b90a8ee1b0824.png

19fb2fa202f1b5c33f4c7a96270d2885.png

ad93aa6cd7ff40e847a672763271bb9e.png

627cfc69314617639daca9ba995576c1.png

最终执行的sql语句打印出来是SELECT * FROM admin WHERE username = '韦小宝' AND password = '222\' OR \'8\'=\'8'

从以上截图就能看出来,由此可见,prepareStatement对象防止sql注入的方式是把用户非法输入的单引号用\反斜杠做了转义,从而达到了防止sql注入的目的

Statement对象就没那么好心了,它才不会把用户非法输入的单引号用\反斜杠做转义呢!

PreparedStatement可以有效防止sql注入,所以生产环境上一定要使用PreparedStatement,而不能使用Statement

当然啦,你可以仔细研究下PreparedStatement对象是如何防止sql注入的,我自己把最终执行的sql语句打印出来了,看到打印出来的sql语句就明白了,原来是mysql数据库产商,在实现PreparedStatement接口的实现类中的setString(int parameterIndex, String x)函数中做了一些处理,把单引号做了转义(只要用户输入的字符串中有单引号,那mysql数据库产商的setString()这个函数,就会把单引号做转义)

大家有兴趣可以去网上,下载一份mysql数据库的驱动程序的源代码,看看mysql数据库产商的驱动程序的源代码,去源代码中找到setString(int parameterIndex, String x)函数,看看该函数中是怎么写的,我没有下载mysql数据库产商的驱动程序的源代码,而是把mysql数据库的驱动程序jar包解压了,找到了PreparedStatement.class文件,利用反编译工具,反编译了一下,如下:

cf33783fc572fdcc4813e4d30d295c9f.png

599a8d1907564e1d1f2f7646384babe2.png

3d8d4be9d4a133d48bb28dabe8d695d0.png

4d8a87e0986beeef6f4dc5751fd19d79.png

2dabe7d77d8a880f30af73f485af77f5.png

7535972f90d25230143dce055ceae2b7.png

85a91e2cdefcfa0eee01d16e7bde0b8b.png

dc21ae4ac67cea56309b6cac0f11adac.png

7113b0dcd880e5a7e75e49d1f472f8f5.png

这下大家应该知道PreparedStatement是如何防止sql注入的了吧

像222' OR '8'='8这样的sql注入还算温柔了,有些更可恶的用户,他们输入的非法的值是delete from tableName或truncate table tableName 这是十分危险的,更有甚者传入drop table tableName;有些数据库是不会让你成功的,但也有很多数据库就可以使这些语句执行,所以生产环境上一定要使用PreparedStatement,而不能使用Statement

下面再举几个例子,看截图

2346bc94b355740e1d71b4393035bec0.png

最终打印SELECT * FROM admin WHERE username = '韦小宝' AND password = '\'; DROP TABLE tableName;#'

ed4dca6a0a1c821b0523dcc0d88f6e1d.png

最终打印SELECT * FROM admin WHERE username = '韦小宝' AND password = '\'; delete from tableName;#'

dafcb2f103f37d7e9a52552b7d585ae1.png

最终打印SELECT * FROM admin WHERE username = '韦小宝' AND password = '\'; truncate table tableName;#'

19c834975a3e762de384850546dd1ef1.png

下面是java代码和sql语句,供大家参考,主要是为了测试PreparedStatement对象,所以java代码写的比较粗略,大家凑合着看吧!

package com.test;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Statement;

/*

* 研究PreparedStatement是如何防止sql注入的,我分析了一下,原来是mysql数据库产商,在实

* 现PreparedStatement接口的实现类中的setString(int parameterIndex, String x)函

* 数中做了一些处理,把单引号做了转义(只要用户输入的字符串中有单引号,那mysql数据库产商的setString()这个函

* 数,就会把单引号做转义)

*/

public class TestConnMySql2 {

public static void main(String[] args) {

String connStr = "jdbc:mysql://localhost:3306/girls";

//String sql = "select * from admin";

String sql = "SELECT * FROM admin WHERE username = ? AND password = ?";

try {

Class.forName("com.mysql.jdbc.Driver");

Connection connection = DriverManager.getConnection(connStr, "root", "root");

System.out.println("数据库连接=" + connection);

//Statement无法防止sql注入

//Statement stmt = connection.createStatement();

//PreparedStatement可以有效防止sql注入,所以生产环境上一定要使用PreparedStatement,而不能使用Statement

PreparedStatement prepareStatement = connection.prepareStatement(sql);

prepareStatement.setString(1, "韦小宝");

//模拟用户输入正常的值

//prepareStatement.setString(2, "222");

//测试sql注入(模拟用户输入非法的值)

prepareStatement.setString(2, "222' OR '8'='8");

/*

*上面那种的sql注入还算温柔了,有些更可恶的用户,他们输入的非

*法的值是delete from tableName或truncate table tableName 这是十分危险的,

* 更有甚者传入drop table tableName;有些数据库是不会让你成功的,但也有很多数

* 据库就可以使这些语句执行,所以生产环境上一定要使用PreparedStatement,而不能使用Statement

*/

//测试sql注入(模拟用户输入非法的值)在mysql中#井号表示单行注释(这是mysql中的基础知识,我就不赘述了)

//prepareStatement.setString(2, "'; DROP TABLE tableName;#");

//测试sql注入(模拟用户输入非法的值)

//prepareStatement.setString(2, "'; delete from tableName;#");

//测试sql注入(模拟用户输入非法的值)

//prepareStatement.setString(2, "'; truncate table tableName;#");

ResultSet rs = prepareStatement.executeQuery();

System.out.println("sql=" + prepareStatement.toString());

int col = rs.getMetaData().getColumnCount();

System.out.println("============================");

while (rs.next()) {

for (int i = 1; i <= col; i++) {

System.out.print(rs.getString(i) + "\t");

if ((i == 2) && (rs.getString(i).length() < 8)) {

System.out.print("\t");

}

}

System.out.println("");

}

System.out.println("============================");

rs.close();

prepareStatement.close();

connection.close();

} catch (ClassNotFoundException | SQLException e) {

e.printStackTrace();

}

}

}

#用户输入正常合法的值

SELECT * FROM admin WHERE username = '韦小宝' AND `password` = '222';

#用户输入正常合法的值

SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = '222';

#sql注入(用户输入非法的值)使用Statement对象,无法防止sql注入(会查询出表的所有数据)

SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = '222' OR '8'='8'

#sql注入(用户输入非法的值)使用PreparedStatement对象,可以有效防止sql注入

SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = '222\' OR \'8\'=\'8'

#sql注入(用户输入非法的值)使用Statement对象,无法防止sql注入(DROP操作很危险)

SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = ''; DROP TABLE tableName;#'

#sql注入(用户输入非法的值)使用PreparedStatement对象,可以有效防止sql注入

SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = '\'; DROP TABLE tableName;#'

#sql注入(用户输入非法的值)使用Statement对象,无法防止sql注入(TRUNCATE操作很危险)

SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = ''; TRUNCATE TABLE tableName;#'

#sql注入(用户输入非法的值)使用PreparedStatement对象,可以有效防止sql注入

SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = '\'; truncate table tableName;#'

#sql注入(用户输入非法的值)使用Statement对象,无法防止sql注入(DELETE操作很危险)

SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = ''; DELETE FROM tableName;#'

#sql注入(用户输入非法的值)使用PreparedStatement对象,可以有效防止sql注入

SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = '\'; delete from tableName;#'

#所以生产环境上一定要使用PreparedStatement,而不能使用Statement

/*

顺便复习一下mysql中的3种注释,我是多行注释

*/

#我是单行注释

-- 我也是单行注释(注意:-- 这种注释,后面必须要加一个空格,否则语法报错)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值