前面介绍了各种SQL语句的调用过程,虽然例子代码写死了每个SQL串,但是完全可以把查询条件作为方法参数传进来。比如现在想删除某个课程的教师记录,那么在编写删除方法时,就把课程名称作为该方法的一个输入参数。据此编写的方法代码示例如下:

	// 删除记录
private static void deleteRecord(Statement stmt, String course) throws SQLException {
String sql = String.format("delete from teacher where course='%s'", course);
int count = stmt.executeUpdate(sql); // 执行处理语句
System.out.println("待执行的SQL语句:"+sql);
System.out.println("删除记录语句的返回结果为"+count);
}

接着外部准备调用上面的deleteRecord方法,第二个课程参数填“化学”,表示希望删除所有化学老师的记录,调用代码如下所示:

			deleteRecord(stmt, "化学"); // 删除记录

运行包含以上代码的测试程序,观察到以下的输出日志。

待执行的SQL语句:delete from teacher where course='化学'
删除记录语句的返回结果为1

从日志信息可见,只删除一条化学老师的记录,看起来似乎一切正常。不过课程参数由外部传入,谁知道课程字符串是什么东西呢?倘若有人闲得发慌,在键盘上随便输了几个字符,像“' or '1'='1”这样的字符串当作课程名称。于是删除方法的调用代码变成了下面这般:

			deleteRecord(stmt, "' or '1'='1"); // 删除记录

再次运行测试程序,发现输出日志变得有点不对劲:

待执行的SQL语句:delete from teacher where course='' or '1'='1'
删除记录语句的返回结果为4

没想到随便输的几个字符竟然也让SQL语句执行了,而且是把teacher表的剩余记录删得精光。这可不得了了,原语句的格式明明只肯删除特定课程的记录,为啥执行结果大相径庭呢?缘由在于待执行的SQL语句呆板地将课程字符串原样填了进去,造成出现“or '1'='1'”这种极端条件,自然MySQL忠实地删光了teacher表。诸如此类的SQL缺陷,人称SQL注入漏洞,它常常被黑客利用为所欲为,造成重大损失。
上述的实验结果暴露了报告机制的安全问题,一旦条件参数被人恶意篡改,就可能产生意料之外的严重状况。为此JDBC又设计了另一种预报告机制,预报告定义了新类PreparedStatement,与原报告Statement不同的是,创建预报告对象时就要设定SQL语句,并且SQL里面的动态参数以问号代替。然后准备调用executeUpdate或者executeQuery之前,先调用预报告对象的setString方法来设置对应序号的参数值。下面便是引入预报告之后的数据库操作代码例子:

	// 测试预报告的处理
private static void testPreparedStatement() {
String sql = "delete from teacher where course=?";
// 先获取数据库的连接,再创建连接的预报告
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword);
PreparedStatement stmt = conn.prepareStatement(sql)) {
//stmt.setString(1, "化学"); // 设置对应序号的参数值
stmt.setString(1, "'' or 1=1"); // 设置对应序号的参数值
int count = stmt.executeUpdate(); // 执行处理语句
System.out.println("预先准备的SQL语句:"+stmt.toString());
System.out.println("删除记录语句的返回结果为"+count);
} catch (SQLException e) {
e.printStackTrace();
}
}

仍以之前的恶意字符串为例,上面代码在调用setString方法时填入了“' or '1'='1”,意图继续浑水摸鱼。运行包含testPreparedStatement方法的测试程序,观察到的日志信息如下所示:

预先准备的SQL语句:com.mysql.cj.jdbc.ClientPreparedStatement: delete from teacher where course='\'\' or 1=1'
删除记录语句的返回结果为0

从日志结果可见,这次捣乱行为没有得逞,一条记录都没删除。注意此时的条件语句变为“course='\'\' or 1=1'”,显然预报告把字符串中的单引号做了转义,使得转义后的条件语句格式不正确,也就没能成功执行SQL。由此证明预报告PreparedStatement提升了数据库操作的安全性,凡是需要动态传入参数的SQL语句,最好采取预报告机制加以处理。


更多Java技术文章参见《Java开发笔记(序)章节目录

Java开发笔记(一百四十九)引入预报告的好处的更多相关文章

  1. Java开发笔记(三十九)日期工具Date

    Date是Java最早的日期工具,编程中经常通过它来获取系统的当前时间.当然使用Date也很简单,只要一个new关键字就能创建日期实例,就像以下代码示范的那样: // 创建一个新的日期实例,默认保存的 ...

  2. Java开发笔记(八十九)缓存字节I/O流

    文件输出流FileOutputStream跟FileWriter同样有个毛病,每次调用write方法都会直接写到磁盘,使得频繁的写操作性能极其低下.正如FileWriter搭上了缓存兄弟Buffere ...

  3. Java开发笔记(二十九)大整数BigInteger

    早期的编程语言为了节约计算机的内存,给数字变量定义了各种存储规格的数值类型,比如字节型byte只占用一个字节大小,短整型short占用两个字节大小,整型int占用四个字节大小,长整型long占用八个字 ...

  4. Java开发笔记(四十九)关键字super的用法

    前面介绍了如何从Bird类继承而来Swallow类,按道理子类应当继承父类的所有要素,但是对于构造方法来说,Swallow类仅仅继承了Bird类的默认构造方法,并未自动继承带参数的构造方法.如果子类想 ...

  5. Java开发笔记(五十九)Java8之后的扩展接口

    前面介绍了接口的基本用法,有心的朋友可能注意到这么一句话“在Java8以前,接口内部的所有方法都必须是抽象方法”,如此说来,在Java8之后,接口的内部方法也可能不是抽象方法了吗?之所以Java8对接 ...

  6. Java开发笔记(六十九)泛型类的定义及其运用

    前面从泛型方法的用法介绍到了泛型的起源,既然单个方法允许拥有泛化的参数类型,那么一个类也应当支持类级别的泛化类型,例如各种容器类型ArrayList.HashMap等等.一旦某个类的定义代码在类名称后 ...

  7. Java开发笔记(七十九)利用反射技术操作私有属性

    早在介绍多态的时候,曾经提到公鸡实例的性别属性可能被篡改为雌性,不过面向对象的三大特性包含了封装.继承和多态,只要把性别属性设置为private私有级别,也不提供setSex这样的性别修改方法,那么性 ...

  8. Java开发笔记(三十)大小数BigDecimal

    前面介绍的BigInteger只能表达任意整数,但不能表达小数,要想表达任意小数,还需专门的大小数类型BigDecimal.如果说设计BigInteger的目的是替代int和long类型,那么设计Bi ...

  9. Java开发笔记(三十二)字符型与整型相互转化

    前面提到字符类型是一种新的变量类型,然而编码实践的过程中却发现,某个具体的字符值居然可以赋值给整型变量!就像下面的例子代码那样,把字符值赋给整型变量,编译器不但没报错,而且还能正常运行! // 字符允 ...

随机推荐

  1. MATLAB画图笔记

    plot函数 plot(x,y)默认格式: 若x,y是向量,则它们必须具有相同的长度.函数将以x为横轴,绘制y. 若x,y都是矩阵,则它们必须具有相同的尺寸,plot函数将针对x的各列绘制y的每列.更 ...

  2. vim文本编辑器——替换、保存退出

    1.替换: (1)全文替换: 利用查询命令查询: (2)指定替换的字符串的范围: 2.保存.退出命令: (1)在命令行模式下保存(:w) (2)另存为(:w+要保存的文件的路径) (3)保存退出(:w ...

  3. Linux(Contos7)下使用SSH远程安装MySQL 8.0.17 完整笔记

    1. 使用putty 配置远程服务器连接,登录服务器. 由于没有指定下载包 使用 yum install mysql-server  提示 未指定包,如: 2. 因为甲骨文的收购了Mysql并且对My ...

  4. 单片机模块化程序: 单片机AT指令配置模块程序模板(非阻塞版)

    拷贝这两个文件到自己的工程 测试1://单片机发送AT+RST\r\n  如果单片机串口接收到OK 或者ready 执行下一条 测试视频: https://qqqqqbucket.oss-cn-bei ...

  5. 洛谷P1962 斐波那契数列题解

    题目背景 大家都知道,斐波那契数列是满足如下性质的一个数列: • f(1) = 1 • f(2) = 1 • f(n) = f(n-1) + f(n-2) (n ≥ 2 且 n 为整数) 题目描述 请 ...

  6. GoCN每日新闻(2019-11-08)

    GoCN每日新闻(2019-11-08) GoCN每日新闻(2019-11-08) 1. Go Modules: v2及更高版本使用 https://blog.golang.org/v2-go-mod ...

  7. 安装和启动ElasticSearch服务遇到的几个问题

    首先安装和启动服务的教程是参考文章:ES入门之一 安装ElasticSearch 然后在最后的启动es服务时遇到了几个小问题,因此在这里记录一下. 因为我对linux并不是很熟悉,因此文中如果有说错的 ...

  8. Linux里使用rz和sz命令

    lrzsz是一个unix通信套件提供的X,Y,和ZModem文件传输协议,官网:http://freecode.com/projects/lrzsz/ windows 需要向centos服务器上传文件 ...

  9. WPF——OXY绘图

    private PlotModel _plotModel; public PlotModel plotModel { get { return _plotModel; } set { _plotMod ...

  10. Eclipse离线安装svn插件

    Eclipse离线安装svn插件 1,下载插件 百度网盘:链接: https://pan.baidu.com/s/1lP7J2_7bdj1Tp4YdnrdllQ 提取码: v3nq 2,在eclips ...