第十二章 JSP基础
从这一章我们开始 JSP 的学习之旅,还是和前面一样,先给大家讲解 JSP 的基础之后就开始我们的黑客技术的研究,并通过代码的分析从中找出 JSP 程序的漏洞。如果大家有 JAVA基础,那么学习 JSP 会感到比较轻松;如果没有学过 JAVA 也没有关系,跟着我的思路一样可以很好的完成 JSP 的基础学习。
大家如果注意观察的话,会发现使用 JSP 的网站数目在逐渐的增加,如图 12-1 所示,利用 google 得到了目前 jsp 网站的数目。而目前使用 JSP 的网站普遍都是大型的网站,这就告诉我们两个方面的信息,一是入侵 jsp 网站的难度相对于 asp、php 网站来说大一些,当然如果入侵成功得到肉鸡的性能也是不一样的,jsp 网站性能相对好一些;二是开源的 jsp系统比较少,也就是说能够下载到的 jsp 系统代码比较少,因为大型网站的 jsp 系统是肯定不开源的。
图 12-1 目前使用 JSP 的网站大概数目
JSP概述
JSP 是 Java Server Pages 的缩写,它是由 SUN 公司倡导,于 1999 年推出的一种 WEB动态网站的开发技术。JSP 充分的利用 JAVA 技术的优势,具有极强的扩展能力和良好的收缩性,与开发平台无关,这是源于 JAVA 的“一次编写,到处运行”的特点,同时也是一项安全技术。它具有良好的动态页面与静态页面分离的能力,通过编译后运行。它一经推出之后就受到了广大程序员的爱好,发展相当的快,现在应该算是第三大脚本语言了。利用 JDBC技术使得 JSP 具有良好的数据库访问能力。我们在 HTML 页面中加入 Java 程序片断就成为
JSP 网页。
相对于其他 B/S 模式下的动态网页技术,JSP 具有以下优势:
(1)、跨平台性:ASP 只能够运行在 Windows 平台下,而 JSP 是基于强大的 Java 语言,可以在几乎所有的操作系统平台下运行。JSP 从一个平台移植到另一个平台,JSP 和 JavaBean甚至不需要重新编译,因为 JSP 被编译后的字节码是与平台无关。
(2)、一次编写,到处运行:JSP 拥有 Java 语言“一次编写,到处运行”的特点,所有的 JSP页面被 JSP 引擎编译成 Java Servlet。这就有了 Java 技术的所有优点,其他的 B/S 模式技术则没有这个优点。
(3)、编写容易,快速上手:学习 JSP 和ASP 一样,可以非常快的上手。当然必须要有 HTML、 javascript 基础,有 Java 基础就是最好的了。
(4)、可重用性:可以将复杂的的处理封装在 JavaBean 或EJB(Enterprise JavaBeans)组件中。再通过 JSP 调用将处理的结果显示出来。这样,一方面使得的开发组件的开发人员可以专注于组件开发;另一方面编写 JSP 的开发人员可以在多处使用组件,而不必关心其实现的细节,而且修改组件只须改动组件内部的设计而不必更改 JSP 代码。从而大大提高了系统的可重用性,在这个方面 PHP 是无法与 JSP 比拟的,而 ASP 支持的组件技术也比较有限,不如
JSP 丰富。
(5)、数据库连接技术:JSP 程序通过 JDBC(Java Database Connectivity)驱动程序与数据库连接。目前很多数据库带有 JDBC 驱动程序,而对于没有自带的,可以利用 JDBC-ODBC 方式来与 ODBC 驱动连接。而绝大多数数据库都代有 ODBC 驱动,这样也使得 JSP 程序可以访问绝大多数的数据库系统。
12.1.1 JSP运行原理
图 12-2 所示的就是 JSP 运行的原理图,当 Web 服务器上第一个 JSP 页面被第一次请求执行时,JSP 引擎首先将 JSP 页面文件转译成一个 Java 文件,即 Servlet。Java Servlet是基于服务器端编程的 API,用Java Servlet 编写的 Java 程序称为 servlet,它通过 HTML与客户端进行交互。服务器将前面转译成的 Java 文件编译成字节码文件,再执行这个字节码文件来响应客户的请求。也正是因为这个字节码使得 JSP 具有跨平台性。当这个 JSP 页面再次被请求时,将直接执行编译生成的字节码来响应,从而加快了执行的速度。这也是为什么 JSP 比 ASP 运行的更快的原因。还有一点大家要清楚:ASP、PHP 等脚本语言都是属于解释性语言,而 JSP 是属于编译性语言。
图 12-2 JSP 运行原理图
第一个JSP页面
在第一章中已经给大家讲授了如何搭建 JSP 服务器,相信大家已经掌握了,如果还有什么不懂的地方可以参考第一章,下面我们就来简单的编写一个 JSP 程序。编写 JSP 代码我们可以使用记事本也可以使用具有强大功能的编辑工具,例如 DreamWare 等。程序的代码如下所示。
<%@ page contentType="text/html;charset=gb2312"%>
<html>
<head>
<title>第一个 JSP 页面</title>
</head>
<body>
<br>
<%
out.println("这是一个简单的 JSP 页面");
%>
</body>
</html>
编写好了上面的代码之后保存,注意的是要保存文件的扩展名为 jsp。在服务器下运行之后就可以看到它的结果了,如图 12-3 所示。
图 12-3 第一个程序运行的结果
在上述程序代码中,JSP 中的 Java 程序片断用“<%”和“%>”括起来了,为了在页面中支持中文的显示,在页面的第一句中加入语句:
<%@ page contentType="text/html;charset=gb2312"%>
如果没有加入这一条语句,那么中文文字将会显示为乱码。至于代码中的 HTML 代码我就不再浪费时间了,如果还有不清楚的黑友可以去参考第三章。要注意的是 HTML 对大小不敏感,但 JSP 程序代码对大小写敏感。out.println 语句的功能就是向浏览器输出括号中的内容,这里输出的是“这是一个简单的 JSP 页面”。
JSP语法
这一节给大家讲授 JSP 的语法知识,这些是进行 JSP 程序漏洞分析的坚固基石,所以最基础是知识,大家一定要融会贯通的掌握它。
在上一节也提到了在 HTML 页面中插入 Java 程序就成了 JSP 页面程序,所以在 JSP 中包含了两种主要的内容,一是 HTML 语言,这是静态内容,二是 Java 程序及其相关元素,这是动态的内容。Java 程序及其相关元素包括了 Java 程序片、表达式、JSP 指令与动作标签以及 Java 变量、方法和类的声明。动态的内容将由 JSP 服务器负责执行,结果转换为字符串,然后把结果和静态内容一起交给客户端的浏览器显示出来。
类
在 Java 中,数据类型可分为两种:简单数据类型和复合数据类型。简单数据类型包括整数、实数、布尔数、字符等,是不能在简化、系统已内置的数据类型,和我们前面将的那些数据类型是一模一样的;复合数据类型就是由简单数据类型组合而成,如类、接口等。
在 Java 中标示符用来标示变量、类、方法、对象、接口和包,需要注意的是它对大小写敏感。
类在第九章(PHP 基础)中已经介绍了,相信大家还没有忘记吧。因为类比较重要,所以这里在多重复一遍哦!在这里大家可以把类理解为一种新的数据类型,一旦声明后,就可以用它来创建对象了。类就是对象的模版,对象就是类的实例,所以要使用对象,先要定义类。类中包括了数据和代码两个部分。数据是类中的实例变量,代码是类中的方法,两者都是类的成员。
下面是一段简单的类声明及对象声明的程序代码。 class ScriptHack{ //定义一个类
int asp; int php; int jsp;
}
ScriptHack hack = new ScriptHack(); //声明一个类的实例,即一个对象
上面的代码首先定义了一个类 ScriptHack,即脚本黑客。它有三个数据:asp、php、 jsp,定义后并不会生成实际的对象实例。所以接下来就是声明这个类的一个实例,即一个对象,对象名称为 hack。大家如果还记得在 PHP 中声明对象的方法的话,会发现 Java 声明对象的有一些不同。在 Java 中声明一个对象的一般形式为:
类名 对象变量 = new 类名(参数 1,参数 2.....);或者
类名 对象变量;
对象变量 = new 类名(参数 1,参数 2......);
声明了对象后,要使用类中的数据或方法用“.”运算符,左边是对象名,右边是数据或方法名,如果要把 hack 的jsp 数据设置为 computer,则用如下的语句: hack.jsp=computer;
类的方法用来实现类所需要的一些功能,定义方法的格式为: type method_name(parameter_list)
{
}
type 指明了方法返回数据的数据类型,如果不返回任何数据则类型为 void。
Method_name 是方法的名称,parameter_list 是传递给方法的参数列表,花括号中间为方法体,即实现方法的程序代码。
有的时候,一个方法需要引用调用它的对象,这个时候我们可以用 this 关键字来替代类名。对于 Java 中的继承,和我们前面第九章中讲的差不多,所以这里对于继承就不在重复了。
(1)、string 类
Java 的字符串常量用双引号("")括起来,是一串字符串。但 Java 中的字符串是作为一个 String 对象来处理的。在 JSP 中,大量的信息是通过字符串来进行交互,所以一定要掌握好 string 类的使用。Java 为 String 类提供了丰富的功能特性,下面给大家举一些常见且教为重要的特性。
①、构造函数
String 类提供了几种构造函数,它的作用是初始化 String 对象的值。我们可以用空的构造函数,表示创建一个新的 String 实例,它是一个空字符串,如下所示:
String s=new String();
也可以用一个字符型数组来构造一个字符串,如下所示的一个程序代码例子,用字符串数组 charstemp 作为字符串 s 的初始值:
char charstemp[]={'a', 'b','c','d','e'}; String s=new String(charstemp);
还可以使用下面的构造函数把一个字符串数组的一部分作为初始值,格式如下: String(char chars[],int startIndex,int numChars);
第一个参数为字符数组,第二个参数为指定部分开始处的数组下标,第三个参数为从第二个参数开始处所取的个数。需要注意的是数组的数据的下标是从 0 开始的,如下所示,我们用字符 bc 来初始化 s。
char charstemp[]={'a', 'b','c','d','e'}; String s=new String(charstemp,1,2);
②、求字符串长度
求字符串长度用 String 类的方法 length(),如下的语句可求出字符串 s 的长度: int slength= s.length(); length()方法返回的是整数型数值。
③、字符串连接
字符串连接使用“+”号即可,返回连接后的字符串,当和其他数据类型连接时,返回数据类型是仍然是字符串,如下所的代码:
out.println("输出一个布尔值:"+true);
此句在浏览器中输出的一个字符串“输出一个布尔值:true”。
④、查找字符串
要在字符串中查找制定的字符或子字符串,可以使用 string 类的两个方法,它们是: indexOf()——查找字符或子字符串在字符串中首次出现的位置。 lastindexOf()——查找字符或子字符串在字符串中最后一次出现的位置。
如果查找成功,方法返回字符或子字符串在字符串中出现位置的下标,如果失败则返回-1。
⑤、修改字符串
用来修改字符串的方法有多个,包括截取子字符串的方法 substring()、连接两个字符串的方
法 concat(),替换字符方法 replace(),去除空格方法 trim()。
substring()方法可以截取子字符串,使用的语法格式有两种。 String substring(int startIndex)
String substring(int startIndex , int endIndex)
上述的两种方法中,参数 startIndex 指定了子字符串开始的下标,参数 endIndex 指定了子字符串的结束下标。第一种形式返回字符串从下标 endIndex 开始直至末尾的子字符串;第二种形式返回从下标 startIndex 开始到下标 endIndex 结束的子字符串。 concat()方法可以用来连接两个字符串,并创建一个新的 String 类的对象,它是连接后的字符串,其使用方法如下。
String concat(string str)
replace()方法用一个字符代替本字符串中某一个特定的在字符串中出现的所有的这个字符,其用法如下所示:
string replace(char originalchar , char replacechar)
用字符 replacechar 代替字符 originalchar,返回替换后的字符串,这个方法在前面已经多次使用了。
最后一个 trim()方法的作用是去掉字符串左右的空格,相信大家对它已经是非常熟悉了。
(2)、StringBuffer 类 String 类表示的是固定长度的字符串,而 StringBuffer 类提供了可变长的字符串,同时还提供了大量的字符串功能。在 StringBuffer 类的字符串中可以再插入字符、追加字符, StringBuffer 会自动增加内存空间,这些都是 String 无法做到的。
①、追加字符或字符串
在 StringBuffer 尾部追加字符或字符串可使用方法 append(),调用的方法有多种,统一表述如下所示:
StringBuffer append(Object obj)
参数 obj 可以在任意类型,比较常用的是一些简单的数据类型以及 String 类。
②、插入字符
用 insert()方法可将一个字符串插入到另一个字符串中。使用的方法有多种,常用的方法形式如下:
StringBuffer insert(int insertindex , char ch) StringBuffer insert(int insertindex , String str)
第一种形式用来在 insertindex 处插入一个字符 ch;第二种形式用来在 insertindex 处插入一个字符串 str。
③、删除字符和字符串
删除字符和字符串的方法有两种,它们调用的语法格式如下:
StringBuffer delete(int startposition , int endposition) StringBuffer deleteCharAt(int deletepostion)
第一个方法 delete()用于删除从 startposition 下标开始到 endposition 下标结束的字符串,返回删除后的 StringBuffer 对象;第二个方法 deleteCharAt()用于删除位于 deletepostion 下标处的字符,两个方法均返回删除后的 StringBuffer 对象。
Java程序片
在 JSP 中,在“<%”和“%>”之间书写的程序代码我们称为 Java 程序片。一个 JSP 页面中可以有多个 Java 程序片。要注意的是 Java 程序片声明的变量在它们所在的 JSP 页面的所有程序片及表达式都有效。
在程序片中声明的变量只在页面有效,是局部变量,它在不同的客户访问统一个页面时,此变量是不能共享的。但如果是在“<%!”和“%>”之间声明的变量就可以在同步的客户之间共享,有效范围是整个 JSP 页面,生命周期是在服务器关闭后变量才会被释放。
用“<%=”和“%>”可以直接输出变量或表达式的值,变量或表达式的值将作为一个字符串在浏览器中输出。这种输出在 JSP 中比较常见,所以大家一定要记住,不然到时候看代码就有点困难哦!
这里我给大家举个例子,示例 12-1 为大家演示了一个网站计数器功能的例子,代码如下所示:
<%@ page contentType="text/html;charset=gb2312"%>
<html>
<body>
<%!int counter=0;
synchronized void counterFunction()
{
counter++;
}
%>
<%counterFunction();%>网站计数器<br>
您是第<%=counter%>位访问者
</body>
</html>
程序首先在“<%!”和“%>”之间声明了一个计数器变量和计数的方法,计数器变量将客户之间的共享,直至服务器关闭。计数方法实际上就是对计数变量增 1 处理,在方法的前面加了 synchronized 关键字。这个关键字可防止客户同时调用此方法时更改计数变量的值时发生冲突,对方法做了串行化处理。一个简单的计数器程序的运行结果如图 12-4 所示。每刷新一次页面,数值会增加 1,也可以同时打开多个浏览器窗口对此页进行测试,可发现变量是共享的。
图 12-4 计数器运行的结果
JSP指令
在 JSP 中,提供了很多指令有了这些指令我们可以很方便的完成一些复杂和特殊的功能,本节就给大家详细讲授常用的且与黑客技术紧密相关的指令。
(1)、page 指令
要定义 JSP 页面的全局属性及其值时使用 page 指令,一般把它放在页面的首部。如:
<%@ page contentType="text/html;charset=gb2312"%>
有了这句之后,在 JSP 页面中就可以显示中文汉字了。指令中的属性值可用单引号或双引号括起来,如果一个属性有多个值就用逗号隔开,在 page 指令中也只有 import 属性可以指定多个值,它用来导入一些程序中要用的包或类,如:
<%@ page import= "java.until.*", "java.awt.*"%>
对于 page 指令最为常用的就是上面的两种了,在这里我也就提这写了。其实 page 指令还是挺复杂的,如果要进行 jsp 开发的,就应该对它有比较深的了解,这个时候你可以参考光盘中自带的电子书,上面对这方面有比较全面的解释。
(2)、include 指令
include 指令用来在该指令处的地方静态的插入一个文件。它只是把文件代码包含过来,与文本组合形成一个大的程序文件。和我们前面将的 include 是一样的功能。在 JSP中它的调用格式为:<%@ include file= "文件路径"%>
示例 12-2 演示了 include 指令的使用过程,这个例子的代码如下所示: Bottom.txt 文件的代码为:
<hr>
<p align="center">
<font size="3">黑客手册版权所有</font><br>
<font size="3">制作人:曾云好</font><br>
<font size="3">联系方式:zengyunhao0@gmail.com</font><br>
</body>
</html> Include.jsp 文 件 的 代 码 如 下 所 示 :
<%@ page contentType="text/html;charset=gb2312"%>
<p align="center">
<%out.println("《精通脚本黑客》");%>
<%out.println("此书出版了,快来看看吧!非安全还有好书等待您哦!");%>
</p>
<%@ include file="Bottom.txt"%>
在本例中,将 Bottom.txt 和 Include.jsp 放在同一个目录下,所以在 include 处直接使用文件名即可。在 Include.jsp 文件中,第 1 句的 page 指令一定要放在主文件中,否则可能会出现部分文件中的中文字符显示为乱码;包含的文件 Bottom.txt 放在末尾,在利用
include 指令之后就相当于把 Bottom.txt 中的代码直接插入到 Include.jsp 文件中去了。代码运行之后的结果如图 12-5 所示。
图 12-5 include 指令使用的结果
JSP动作指令
与JSP 指令不同,JSP 动作指令将影响 JSP 运行时的功能,对 JSP 程序有很大的影响,在这一节中大家一起来认真学习吧。 (1)、include 动作指令
include 动作指令用来在 JSP 页面中动态包含一个文件,这样包含的页面程序与被包含页面的程序是彼此独立的,互不影响。JSP 的 include 动作指令可包含一个动态文件也可以包含一个静态文件。如果包含的是一个静态文件(如一个文本文件),就直接输出给客户端,由客户端浏览器负责显示;如果包含的是一个动态文件,则由服务器的 JSP 引擎负责执行,把运行结果返回给客户端显示出来。需要注意的是:include 动作指令与 include 指令不同,后者是静态包含,取包含文件与被包含文件组合形成一个文件,而前者是动态包含,原理不同,使用时也会有差别。
include 动作指令的使用格式如下:
<jsp:include page="文件路径"/>或者
<jsp:include page="文件路径">
<jsp:para name="参数名 1" value="参数值 1"/>
.......
<jsp:para name="参数名n" value="参数值 n"/>
</jsp:include>
不 需 要 传 递 参 数 时 , 这 两 种 使 用 的 形 式 效 果 是 一 样 的 , 如 果 要 传 递 参 数 就 要 使 用 第 二 种 形 式 了 。 (2) 、 useBean 动 作 指 令
这是一个非常重要的动作指令,用来在 JSP 中创建并使用一个 JavaBean(对于 JavaBean 在后面会详细给大家介绍,在实际工程中常用 JavaBean 做组件开发)。在 JSP 中声明一个
JavaBean 的语法格式如下所示:
<jsp:useBean id="bean 的名称" scope="有效范围" class="包名.类名">
</jsp:useBean>
其中,id 参数是在 JSP 中给这个 bean 组件的名字,只要是它的有效范围内,均可使用这个名称来调用它;scope 为 bean 的有效范围,它的取值有四种:page、request、session、 application,默认情况下取值为 page,值为 page 表示在当前 JSP 页面及当前页面所包含的静态文件中有效,值为 request 表示有效范围仅在 request 期间;值为 session 表示在与
每个客户会话期间有效;值为 application 表示所有客户端共享这个 bean,直至服务器关闭才取消这个 bean;class 参数中,如果类属于某个包则类名前面要加上包名,中间用“.”引用,否则直接用类名即可。
(3)、setProperty 动作指令
这个动作指令用来设置 Bean 中的属性的值,基本语法格式有如下四种:
<jsp:setProerty name= "bean 的名称" property= "*"/>
<jsp:setProerty name= "bean 的名称" property= "属性名称"/>
<jsp:setProerty name= "bean 的名称" property= "属性名称" param="参数名称"/>
<jsp:setProerty name= "bean 的名称" property= "属性名称" value= "属性值"/>
第一种语法格式中 property= "*",应用这种格式要求 bean 属性的名称与类型要和
request 对象中参数的名称与类型一致,用此 bean 中的属性来接收客户输入的数据,系统会根据名称来自动匹配。如果类型不一致,会根据 bean 中的类型进行转换。
第二种语法格式则只设置其中匹配的一个 bean 的属性。
第三种语法格式根据指定的 request 对象中的参数与属性匹配。
第四种语法格式用来给 bean 的属性赋值,属性值的数据类型要与属性的数据类型一致,否则会出错。
SetProperty 动作指令可以在 useBean 动作指令中使用,也可以在声明了 useBean 后使用,但不能在声明之前使用。与 useBean 动作指令结合使用的格式如下:
<jsp:useBean id="bean 的名称" scope="有效范围" class="包名.类名">
<jsp:setProerty name= "bean 的名称" property= "属性名称" value= "属性值"/>
..........
<jsp:setProerty name= "bean 的名称" property= "属性名称" value= "属性值"/>
</jsp:useBean>
(4)、getProperty 动作指令
getProperty 动作指令用来获得 bean 的属性并将其转换成字符串,再向 JSP 页面中输出。使用的语法格式如下:
<jsp:getProperty name= "bean 名称" property= "属性名称"/>或者
<jsp:getProperty name= "bean 名称" property= "属性名称">
<jsp:getProperty>
内置对象
JSP 中的内置对象和我们前面在 ASP 中所学的对象是一样的,在 JSP 中内置对象有: request、response、session、application、out、config、pageContext。其中 request、
response、session、application 在 ASP 中我们已经学过了,在 JSP 中他们的功能也是和
ASP 是一样的,所以对于这几个对象我就重点给大家讲解它们的方法。
request对象常用方法
request 对象封装了客户端提交的数据信息,包括用户提交的信息以及客户端的一些
信息。客户端可通过 HTML 表单或者在网页地质后面带参数的方法提交数据,再用 request对象的相关方法来获得提交的各种数据,对于 request 对象的更多基础知识如果还有不懂的地方可以参考第 6 章的 6.2.1 小节。
request 的方法主要用来处理客户端浏览器提交的请求中的各项参数和选项。常用的方法如下:
(1)、getCookie()
此 方 法 用 来 获 得 客 户 端 的 cookie 对 象 , 返 回 值 为 一 个 cookie 数 组 , 调 用 的 语 法 格 式 如 下 : request.getCookie() (2) 、 getMethod()
这 个 方 法 用 来 获 得 request() 的 方 法 , 如 get 或 post 等 , 调 用 的 语 法 格 式 为 : request.getMethod() (3) 、 getParameter()
这个方法用来获得指定名称的客户端提交的参数的值,调用的语法格式如下所示: request.getParameter(String str)
参数 str 是指定的参数的名称。这个方法是 JSP 程序中最为常用的方法之一,正是有了它,
JSP 才能够获得客户端浏览器提交的数据。 (4)、getParameterValues()
此方法获得指定的客户端提交的参数的值,调用的语法如下: request.getParameterValues(String str) (5)、getRequestURL()
此方法用来获得发送请求字符串的客户端的地址。调用的语法格式如下: request.getRequestURL() (6)、getRemoteHost()
这个方法用于得到客户端的机器名称,如果得不到名称则得到客户端的 IP 地址,调用格式为:
request.getRemoteHost()
(7)、getRemoteAddr()
此方法用于得到客户端的 IP 地址,调用格式为: request.getRemoteAddr()
(8)、getServlePath()
这个方法返回客户端请求的页面的文件目录,调用格式为: request.getServlePath() (9)、getServerName()
这个方法用于得到服务器机器的名称,调用方法为: request.getServerName()
上面的就是 request 对象最为常用的方法,下面就是我利用上面的方法写的一个小程序,算是对上面的方法的一个运用吧,代码如下所示:
<%@ page contentType="text/html;charset=gb2312"%>
<html>
<head>
</head>
<body>
请求信息如下:<br>
请求的方法是:<%=request.getMethod()%>
<br>
请求的 URI 是:<%=request.getRequestURI()%>
<br>
接受客户提交信息的页面路径是:<%=request.getServletPath()%>
<br>
服务器名称是:<%=request.getServerName()%>
<br>
客户端 IP 地址是:<%=request.getRemoteAddr()%>
<br>
客户端机器的名称是:<%=request.getRemoteHost()%>
</body>
</html>
程序代码用了 request 的各种方法,输出 request 中的各种信息。运行结果中得到客户端的 IP 地址是 127.0.0.1,这里因为是在我本地的电脑上调试的,127.0.0.1 代表本地机器,故得到了此 IP 地址,其程序代码运行的结果如图 12-6 所示。
图 12-6 利用 request 的方法得到的各种信息
response对象常用方法
与 resquest 对象对应的是 response 对象,response 对象可以用来对客户的请求做出响应,向客户端发送数据。输出的数据可以是各种数据类型,甚至是文件,这可以通过
page 指令的contentType 属性或response 的setContentType()方法来设置。对于 response对象的更多基础知识如果还有不懂的地方可以参考第 6 章的 6.2.2 小节。
response 对象的常用方法如下所示:
(1)、addCookie()
这个方法用来添加一个 cookie 对象,以保存客户端的用户信息,cookie 对象的值只能是字符串,调用的语法格式如下:
response.addCookie(Cookie cookname)
参数 cookname 是有一个 Cookie 对象的名称。如果对 Cookie 的基础知识还不太熟悉的话,可以再次参考第 4 章的 4.5 节。
(2)、sendRedirect()
这个方法用于对页面进行重定向,调用的语法格式为: response.sendRedirect(String url)
url 这个参数可以是合法的 url 地址,也可以是文件路径。
(3)、setHeader()
这个方法用于设置指定的 HTTP 头信息的值,如果同名的头信息已经存在则会被覆盖。调用的语法格式为:
response.setHeader(String headername,String headervalue)
方法中的 headername 是头信息的名称,headervalue 是对应的头信息的值。
下面举个例子来说明一下上面的方法的运用,这里我就用 sendRedirect()来具体说明,程序的代码如下:
<%@ page contentType="text/html;charset=gb2312"%>
<html>
<body>
<%//页面重定向程序片 String url;
url=request.getParameter("goaddress"); if(url!=null)
{
response.sendRedirect(url);
}
%>
<form name="form1" action="sendRedirectExample1.jsp" method="post" >页面重定向:
<select name="goaddress" onchange="javascript:form1.submit()">
<option value="">========请选择========</option>
<option value="http://www.nohack.cn">黑客手册官方网站</option>
<option value="http://www.eviloctal.com">邪恶八进制</option>
<option value="http://www.pediy.com">看雪</option>
<option value="http://www.xfocus.net">安全焦点</option>
</select>
</form>
</body>
</html>
在程序中,声明一个表单,把数据提交给本页,当改变下拉选择框的选项时回提交表单;在程序片中,接收到提交的表单中的转向地址后,用 response 对象的sendRedirect()方法进行页面的重定向;在程序片中要判断接收到的参数是否为空,因为数据是提交给本页面的,当表单数据没有提交时,得到相应数据项的数据会为空,这个时候并不发生页面的重定向。上面的程序的运行结果如图 12-7 所示。
图 12-7 页面重定向
session对象常用方法
Session 对象用来保存一些需要在与每个用户会话期间保持的数据信息。这样就方便会话处理工作。如可以用 session 变量记住用户的用户名,就不必在其他的网页中重复输入了。
Session 对象的信息保存在服务器中,但 ID 保存在客户机的 cookie 中,如果客户机不支持
cookie 则转为 URL 重写。
下面我们来看看 session 对象的一些常用的方法:
(1)、getAttribute()
这个方法可以获得制定的 session 对象,调用方式为: session.getAttribute(String name)
参数 name 是制定的 session 对象的名称。方法返回一个 object 对象,任何对象都可以加入到 session 中。
(2)、getCreationTime()
此方法得到 session 对象的创建时间,该方法的调用格式为: Session.getCreationTime()
此方法返回的是一个 long 类型的值,单位为毫秒,是从 1970 年 7 月 1 日午夜起至 session对象创建为止所隔离的毫秒数。
(3)、getId()
这个方法用于得到 session 对象的 ID 号,调用语法如下: Session.getId()
每个客户端与服务器会话的 ID 号不一样的,服务器通过这个 ID 号来区分不同的用户。
(4)、getMaxIncativeIterval()
这个方法是用于获得 session 对象的生存时间,返回值是一个 int 型值,单位为秒,调用的语法如下:
Session.getMaxIncativeIterval()
(5)、setAttribute()
这个方法用来设置指定 session 对象中的数据对象的值,调用语法为: Session.setAttribute(String name,Object value)
application对象常用方法
Application 对象用来在多个程序或者是多个用户之间共享数据,用户使用的所有
application 对象都是一样的,这与 session 对象不同。服务器一旦启动,就会自动创建
application 对象,并一直保持下去,直至服务器关闭,application 就会自动消失。还是一样,我们来学习 application 对象的一些方法。
(1)、getAttribute()
这个方法得到指定的 application 对象中的数据对象的值,调用格式为: application.getAttribute()
(2)、getInitParameter()
此方法返回指定的 application 对象中的数据对象的初始值。调用语法格式为:
application.getInitParameter(String name)
参数 name 是指定 application 对象中的数据对象的名称。
(3)、getServletInfo()
用于得到 servlet 编译器当前版本的信息,调用方式为: application.getServletInfo()
(4)、setAttribute()
这个方法用于设置指定名字 name 的application 对象的属性值 object。调用的语法如下: application.setAttribute(String name,Object object)
第一个参数 name 是指定 application 对象中的数据对象的名称,第二个参数 object 是这个对象对应的值,其类型为 object,说明可以是任意类型。如果对象不存在,则添加,如果已存在则覆盖。
out对象简介
out 对象用来向客户端输出数据,在前面已经使用了它了。最为常用的方法就是 print()及 println()方法。下面就来具体看看它常用的方法。
(1)、clear()
这个方法是清除缓冲区中的数据,并不输出,使用很简单: out.clear()
(2)、clearBuffer()
这个方法也是清除缓冲区中的数据,但是它还把缓冲区中的数据输出到客户端,调用格式为: out.clearBuffer()
(3)、close()
这方法用来关闭输出流,因为 out 对象本身实际上是一个输出流。调用方式为: out.close()
(4)、flush()
此方法是输出缓冲区中的数据内容,调用语法如下: out.flush()
(5)、getBufferSize()
用于得到缓冲区的大小,调用格式为: out.getBufferSize()
(6)、print()与 println()
print()与 println()方法应用的最为广泛,他们的作用都是向客户端输出数据,他们二者的差别是 println()方法在输出数据的同时还会输出一个换行符号。下面给大家举个例子来说明,代码如下所示:
<%@ page contentType="text/html;charset=GB2312" %>
<HTML>
<head>
<title>out 应用示例</title>
</head>
<BODY>
<%
out.print("<table border='1' width='100%'><tr><td width='100%' colspan='4'>");
out.print("《精通脚本黑客》参与人员(用 out 输出表格)"+"</tr>"); out.print("<tr><td width='25%'>"+"姓名"+"</td>");
out.print("<td width='15%'>"+"年龄"+"</td>"); out.print("<td width='15%'>"+"性别"+"</td>"); out.print("<td width='45%'>"+"工作单位"+"</td></tr>"); out.print("<tr><td width='25%'>"+"曾云好"+"</td>"); out.print("<td width='15%'>"+"20"+"</td>"); out.print("<td width='15%'>"+"男"+"</td>");
out.print("<td width='45%'>"+"湖南省长沙市"+"</td></tr>"); out.print("<tr><td width='25%'>"+"土豆"+"</td>"); out.print("<td width='15%'>"+"33"+"</td>");
out.print("<td width='15%'>"+"男"+"</td>"); out.print("<td width='45%'>"+"黑客手册"+"</td></tr>"); out.print("</table>");
%>
</BODY>
</HTML>
程序利用 out 对象的print()方法输出一系列的表格元素。在实际应用中,不仅是表格元素,任意 HTML 元素都可以利用 out 对象输出。上面的代码的运行结果如图 12-8 所示。
图 12-8 利用 out 对象的 print()方法输出表格信息
JSP 中还有一些其他的内置对象,如 page、config 等。Page 对象仅用来保存脚本语言不是 Java 时的时间;config 对象实质上是 servletConfig 类的一个对象,用来配制处理 JSP页面的句柄。这些对象很少用到,和我们的安全也不是联系的很紧密,所以这里对他们两个就稍微提一下,如果要进行 JSP 开发的话可以去学一下,光盘中已经为大家准备了很好的
JSP 电子书籍,以供大家系统性的学习 JSP 系统开发。
文件操作
在进行 JSP 开发中,会频繁的使用文件操作,利用它可以实现很多功能。所以同时这也是常常出现安全问题地方,利用它们的漏洞我们可以成功的进行入侵活动。下面就开始我们的文件操作的学习吧,打起精神,跟上进度哦!
文件类
在Java 的io(输入输出)类中大多数是流式的,但是文件类(File 类)不是。File 类并没有指定信息怎样从文件读取或向文件存储,而是描述了文件本身的属性,如文件的权限、
时间、日期、目录路径、目录层次结构等,目录也被当成是一个 File。File 类的构造函数有三种形式:
File(String Path)
File(String Path,String filename) File(File dirObj,String filename)
第一种与第二种形式中的参数 path 是文件的路径名;参数 filename 是文件名;dirObj是一个制定目录的 File 对象。如:
File file1 = new File("/");
File file2 = new File("/","newfile2.txt"); File file2 = new File(file1,"newfile2.txt");
第一句语句生成一个 file 对象,指向参数所示的路径;第二句语句有两个参数路径和文件;第三句语句的第一个参数为 file1 对象,第二个参数为指向的文件,所以实际上是
file2 与file3 是指向同一个文件。
File 类定义了很多获取 file 对象标准属性的方法,下面我们来学习一些比较重要的方法。
(1)、canRead()
这个方法用来判断文件是否可读,可读返回 true,否则返回false。调用语法如下: fileObjectName.canRead()
其中,fileObjectName 是利用 File 类生成的对象的名称,下面也是一样的含义。
(2)、canWrite()
这个方法用来判断文件是否可写,可写返回 true,否则返回false。调用语法如下: fileObjectName.canWrite()
(3)、exists()
此方法用于判断文件是否存在,存在返回 ture,否则返回 false。调用语法如下: fileObjectName.exists()
(4)、getAbsolutePath()
该方法用于返回文件的绝对路径,是一个字符串类型,语法如下: fileObjectName.getAbsolutePath()
(5)、getName()
该方法用于得到文件的名称,语法为: fileObjectName.getName() (6)getParent()
用于得到文件的父目录的名称,调用语法如下: fileObjectName.getParent()
(7)、lastModified()
此方法得到文件最后一次修改的时间。函数的返回值是一个长整型数值,是从 1970 年午夜至文件最后一次修改时间为止相隔的毫秒数,调用的语法如下: fileObjectName.lastModified()
(8)、renameTo()
重命名文件,调用格式为: fileObjectName.renameTo(File newObjectName)
这个方法把所调用的 File 对象更改为由 newObjectName 指定的文件名。如果成功则返回
ture,如果试图对文件从一个目录转到一个目录,或者使用了一条已经存在的文件名则返回
false,表示不成功。
不过需要注意的是,如果要用到上面的方法,必须在文件的头部中引用 java.io 包,因为文件操作的相关类均放在这个包中,即<%@ page import= "java.io.*"%>。
用 File 类不仅可以操作文件,还可以操作目录。下面就来简单介绍一下如何操作目录。要创建一个新目录的方法 mkdir(),创建成功返回 ture,失败返回 false。如果所要创建的目录已存在也是返回 false。调用的格式为:
fileObjecName.mkdir()
下面的代码就是在 D:/下创建一个新的目录 hack:
<%
File file1=new File("D:/", "hack"); file1.mkdir();
%>
如果要创建一个路径不存在的目录则使用 mkdirs()方法。
使用 File 类的对象的 list()方法可以提取该目录内部其他文件和目录的列表。这个方法有两种调用形式,调用格式为:
fileObjectName.list() fileObjectName.listFiles()
第一种形式返回的是此目录下全部文件的文件列表,即一个字符串数组;第二种形式返回一个文件类型数组,是目录下的所有文件。
调用 delete()方法即刻删除指定文件或文件目录,成功则返回 ture,否则返回 false。
字节流
在 Java 中主要有四个输入/输出的抽象类,InputStream、OutputStream、Reader 及
Write。InputStream 和 OutputStream 为字节流设计;Reader 和 Write 为字符流设计。一般,处理字符或字符串时应使用字符流类,处理字节或二进制对象时应用字节流类。
(1)、字节输入流
字节输入流都是 InputStream 类的子类,是一个定义了 Java 流式字节输入模式的抽象类。我们在进行文件操作时要使用到 FileInputStream 类,即文件输入类。
InputStream 类的常用方法有:
①、available()
这个方法用于得到当前可读的输入字节数,调用的语法为: InputStreamObjectName.available()
其中,InputStreamObjectName 是InputStream 类被继承实现后的类的对象的名称。
②、close()
此方法用于关闭输入源。输入源在使用了这个方法关闭后如果继续读取就会产生异常。调用的语法如下:
InputStreamObjectName.close()
③、read()
这个方法用于读取数据,它有三种调用形式,如下所示: InputStreamObjectName.read() InputStreamObjectName.read(byte buffername[])
InputStreamObjectName.read(byte buffername,int offset,int len)
第一种形式读取但个字节的数据;第二种形式从输入源中读取字节数组 buffername 的长度
(buffername.length)的字节到 buffername 中;第三种形式从输入源中读取 len 长度的字节数据,并把数据在 buffername 这个字节数组中从 offset 下标位置开始存放。
FileInputStream 类的所有方法均是从 InputStream 类继承而来,可从文件读取字节。它的构造函数可生成 FileInputStream 类的对象,有两种形式:
FileInputStream(String filepath) FileInputStream(File fileObjectname)
第一种形式中的 filePath 参数是传见文件输入类对象所指向的文件的路径,即输入源。第二种形式中的 fileObjectname 是文件类对象的名称,用这个文件类对象所指向的文件作为输入源。生成文件输入类对象后就可用上面的方法进行数据操作了。如下面所示的代码,都是使用上面两种构造函数的形式指向同一个输入流。
FileInputStream file1=new FileInputStream("D:/hack.txt") File file2=new File("D:/hack.txt");
FileInputStream inputfile=new FileInputStream(file2)我们可以利用它来调用上面的方法,如: inputfile.read(); //读取字节数据
上面的操作一次只允许对一个字节进行操作,在 Java 中,缓冲流把内存缓冲器连接到输入/输出流,这样就允许同时对多个字节进行操作。通过使用 BufferedInputStream 和
BufferedOutputStream 类可实现缓冲流。 文件输入操作用 FileInputStream 类与
BufferedInputStream 类结合使用可大大提高性能。BufferedInputStream 类的构造函数有以下两种形式:
BufferedInputStream(InputStream InputStreamName) BufferedInputStream(InputStream InputStreamName,int BufferSize)
参数 InputStreamName 是输入流类的名称,BufferSize 是指定的缓冲区的大小,以字节为单位。
BufferedInputStream 类可以使用 InputStreamName 中的方法,如下的代码可把缓冲流与输入流联系起来:
File file1 = new File("D:/test.txt"); FileInputStream inputfile=new FileInputStream(file1);
BufferedInputStream buffer1=new BufferedInputStream(inputfile);
(2)、字节输出流
字节输出流都是 OutputStream 类的子类,是一个定义了 Java 流式字节输出模式的抽象类。输出流的指向是数据输出的目的地,在 Java 程序中就通过输出流来实现。
OutputStream 类常用的方法如下所示:
①、close()
该方法关闭输出流,如果关闭后还进行写操作的话会产生异常情况。调用格式为: OutputStreamObjectName.close()
其中,
②、flush()
这个方法用来刷新输出缓冲区,调用语法为: OutputStreamObjectName.flush()
③、write()
这个方法为写数据,调用的方式有三种,分别如下: OutputStreamObjectName.write(int b) OutputStreamObjectName.write(byte buffername[]) OutputStreamObjectName.write(byte buffername[],int offset,int len)
第一种形式向输出流写入单个字节,参数是一个整数型;第二种形式向一个输出流写一个完整的字节数组;第三种形式把 buffername 字节数组中从 offset 位置开始的 len 长度的字节写入。
FileOutputStream 类创建了一个可以向文件写入字节的类 OutputStream,它的构造函数有以下三种形式:
FileOutputStream(String filePath) FileOutputStream(File fileObjectName) FileOutputStream(String filePath, boolean append)
参数 filePath 是文件的全称路径,参数 fileObjectName 是一个文件的 File 对象,append是布尔类型数据,当其值为 ture,表示追加。如下面的代码,利用上面的构造函数构造一个输出流并使用它的方法:
FileOutputStream fopt=new FileOutputStream("D:/fopt.txt"); fopt.write(3);
fopt.close();
BufferedOutputStream 类是用于输出缓冲的缓冲流类,OutputStream 类中的方法可以在其中使用。BufferedOutputStream 的构造函数和 BufferedInputStream 的是一样的,只要把 BufferedInputStream 改成 BufferedOutputStream 即可。
字符流
字节流已经为输入/输出处理提供了强大的处理功能,不过在处理一些比如汉字的时候可能就会出现乱码,因为一个汉字实际上是占两个字节的存储空间。为此 Java 提供了字符流类,字符流类的顶层是 Reader 和Writer 类。
(1)、字符输入流
所有的字符输入流类都是 Reader 类的子类。Reader 类常用的方法如下:
①、close()
该方法作用是关闭输入源,调用语法如下: ReaderObjectName.close()
ReaderObjectName 是生成的 Reader 类对象的名称。
②、read()
这个方法是用来读取字符数据,它的用法和前面的字节输入流中的 read()是一样的,只要把原来的 InputStreamObjectName 改成 ReaderObjectName 即可。
Reader 类还不能直接使用,必须继承它才能够使用。FileReader 类可以用于读取文件,继承自 Reader 类,它有两种构造函数的形式:
FileReader(String filePath) FileReader(File fileObjectName)
参数 filePath 是一个文件的完整路径;fileObjectName 是指向文件的文件类对象的名称。
(2)、字符输出流
所有的字符输出流类都是 Writer 类的子类,所有的字符输出流的方法和字节输出流
是一样的,只要把 OutputStreamObjectName 改成 WriterObjectName 即可。大家现在应该也猜的到 WriterObjectName 是生成的 Writer 类对象的名称。
FileWriter 类是一个用来写文件的 Write 类,继承自 Write 类。它的构造函数同样和字节输出流是一样,只要把 FileOutputStream 改成 FileWriter。
JDBC接口
通过第 5 章的学习,我想大家对 SQL 数据库还是比较熟悉了。而这一节就来给大家讲解一些 JSP 下特有的数据库方面的知识。在 JSP 中主要使用 JDBC(Java Database Connectivity,Java 数据库连接)技术来连接数据库的。
JDBC 与 ODBC(OpenData Base Connectivity,开放式数据库连接)的作用非常类似,它在应用程序和数据库之间起到桥梁的作用,利用他们才能够对数据库进行各种操作。因为
ODBC 比 JDBC 应用的更加广泛,所以为了解决有的数据库没有提交 JDBC 接口,SUN 公司开发了 JDBC-ODBC 桥,这样使得 JDBC 可以连接几乎所有的数据库了。
在 JSP 中如果要使用后台数据库的话,那么在 JSP 页面的最前面应该要使用 java.sql.*包,方法为<%@ page import= "java.sql.*" %>。这是因为关于数据库的各种操作函数都已经被包含在 java.sql.*包中了,只有引用了它才可以对数据库进行各种操作。
(1)、Driver
在第 5 章已经给大家讲授了如何建立 ODBC 数据源,如果我们已经建立了一个数据源,那么就可以在 Java 程序中通过 JDBC-ODBC 接口连接数据库了。无论是用 JDBC-ODBC 还是 JDBC,都需要用的 Driver(驱动)。我们可以用 Class.forname()方法来加载驱动程序。
如果使用的是 JDBC-ODBC 的方法,则使用如下的语句: Class.forname("sun.jdbc.odbc.jdbc.OdbcDriver");如果使用的是 JDBC,则用以下的形式: Class.forName("jdbc.driver_class_name");
其中,driver_class_name 是驱动的类名。比如下面的 SQL Server 和MySQL 数据库。 Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver"); //SQL Server Class.forName("org.gjt.mm.mysql.Driver");
(2)、DriverManager
用 DriverManager 的 getConnection()方法我们可以建立一个数据库连接对象,利用它我们就可以和后台的数据库进行信息交互了,它的一般形式如下: DriverManager.getConnection("驱动类型:数据源", "用户名", "密码")
这个方法返回一个连接,一般用一个连接对象接收返回对象,如下所示:
Connection connection_name= DriverManager.getConnection("驱动类型:数据源", "用户名", "密码")
其中,connection_name 是程序员给的连接名称。
当驱动类型是 JDBC-ODBC 时用 JDBC:ODBC,是 JDBC 我们就用 JDBC。还有就是不同的数据库的连接可能会有一些不同,下面一起来看看 SQL Server 和MySQL 数据库。
SQL Server 数据库的连接为:Connection conn = DriverManager.getConnection("jdbc:m
icrosoft:sqlserver://主机:端口号;DatabaseName=数据库名", "用户名", "密码")
比如我们要连接我自己本地机器的 hack 数据库,用 sa 用户名,密码为 hack,则用如下的语句: Connection conn = DriverManager.getConnection("jdbc:microsoft:sqlserver:
//localhost:1433;DatabaseName=hack", "sa", "hack");
对于 MySQL 数据库的连接为:Connection conn = DriverManager.getConnection("
jdbc:mysql://主机:端口号:数据库名", "用户名", "密码")
连接数据库的时候可能会不成功,所以常常在程序中捕获异常。对于异常处理,JSP里我并没有提及,这是因为在第 9 章 PHP 基础中已经讲了,这里不重复了。异常处理也可以算是一种安全技术,和我们的黑客技术还是有一些关系,不过有前面对异常处理的基础知识了解了就足够了,在后面我会给大家讲解一个关于利用异常处理漏洞的例子给大家看看。还是回到我们的数据库中来吧,在数据库中为了防止连接数据库时出现异常,引用了异常处理代码,如下所示:
Try
{ //这里引用的是数据库连接的代码,如:
Connection conn = DriverManager.getConnection("jdbc:microsoft:sqlserver://localhost:1433;Dat abaseName=hack", "sa", "hack");
}
Catch(SQLException e) // SQLException 是专门用来处理数据库异常的,由系统提供
{
}
(3)、Connection
一个 connection 表示与一个特定数据库的会话,connection 类的常用方法如下所示:
①、close()
用来立即释放 connection 的数据库和 JDBC 资源,还有就是如果发生了一个致命错误时也将使 connection 关闭,调用方法为:
connectionObjectName.close()
connectionObjectName 是生成 connection 类对象的名称。
②、createStatement()
在程序中我们可以用 connection 类的对象的方法 createStatement()来创建 SQL 语句,如下所示:
Statement sql_statement_name=connection_name.createStatement();
sql_statement_name 是新创建的 Statement 类对象的名称,connection_name 是已有的数据库连接对象的名称。用这种方法返回的 SQL 语句可用于执行基本的 SQL 语句。 (4)、Statement
Statement 类对象代表 SQL 语句,用于将 SQL 语句发送数据库。Statement 对象用于执行一条 SQL 语句并获得它的结果,在任何时候每条语句仅能打开一个 ResultSet,ResultSet是语句执行后返回的记录结果集。Statement 类的常用方法如下:
①、close()
用来关闭语句,同时立即释放该语句的数据库和 JDBC 资源,如果它有相应产生的 ResultSet,则 ResultSet 也被关闭。调用方法为:
StatementObjectName.close()
其中 StatementObjectName 是Statement 类的对象的名称。
②、execute()
用来执行一条可能返回多个结果的 SQL 语句。返回值是一个布尔值,如果下一个结果是
ResultSet,返回 ture;如果它是一个更新数量或没有其他结果,返回 false,调用方法为: StatementObjectName.execute(sqlstring)
其中,sqlstring 是一个SQL 语句
③、 executeQuery()
用来执行一条返回单个 ResultSet 的语句,返回值是由查询产生的数据的 ResultSet。调用方法为:StatementObjectName.executeQuery(sqlstring)
其中,sqlstring 是静态的 SELECT 语句
④、executeUpdate()
执行一条 INSERT、UPDATE 或 DELECT 语句或是没有返回值的 SQL 语句,调用方法为: StatementObjectName.executeUpdate(sqlstring)
其中,sqlstring 是一条INSERT、UPDATE 或DELECT 语句或是没有返回值的 SQL 语句。
⑤、getResultSet()
用于得到当前 ResultSet 的结果,调用方法为: StatementObjectName.getResultSet()
前面我们已经多次提到了 ResultSet,其实 ResultSet 类是用于接收执行 SQL 查询语句得到的记录集。它用 get 方法可得到记录中不同列的数据,用它的 next()可把当前记录指针往下移动一行。在 ResultSet 类中含有很多 getXXX()方法,他们的作用都是获得当前行的列值,例如 getInt()函数作用是把当前行的列值作为一个 Int 值获取;getString()函数作用是把当前行的列值作为一个 String 值获取。
下面我给大家举个例子,用来说明我们上面所介绍的知识,代码如下:
<%@ page contentType="text/html;charset=GB2312" %> //JSP 指令
<%@ page import="java.sql.*" %> //包含 sql 包,用于数据库操作
<HTML>
<BODY>
<% Connection con; //创建一个 connection 类的对象 conn Statement sql; //创建一个 Statement 类的对象 sql ResultSet rs; //创建一个 ResultSet 类的对象 rs try //异常处理
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //加载驱动
}
catch(ClassNotFoundException e)
{
out.print("类找不到!"); //出现异常情况时的处理
}
try
{
con=DriverManager.getConnection("jdbc:odbc:testDB");
//创建一个数据库连接对象
sql=con.createStatement();
//利用connection 类的 createStatement()方法创建 SQL 语句对象 rs=sql.executeQuery("SELECT * FROM userTable");
//执行SQL 语句,并返回到 ResultSet 类的对象rs out.print("<Table Border>"); //输出表格
out.print("<TR><td colspan=8 align=center>用户数据</td></tr>"); out.print("<TR>");
out.print("<Td width=100 >"+"用户 ID 号"); out.print("<Td width=50 >"+"用户名"); out.print("<Td width=100>"+"用户真实姓名"); out.print("<Td width=50>"+"年龄"); out.print("<Td width=50>"+"性别"); out.print("<Td width=100>"+"联系地址"); out.print("<Td width=100>"+"联系电话"); out.print("<Td width=100>"+"添加时间");
out.print("</TR>");
while(rs.next())
{ out.print("<TR>");
out.print("<TD >"+rs.getLong(1)+"</TD>");
//利用ResultSet 类的 getLong()方法获得当前行的列值 out.print("<TD >"+rs.getString(2)+"</TD>");
//利用 ResultSet 类的 getString()方法获得当前行的列值 out.print("<TD >"+rs.getString(4)+"</TD>");
out.print("<TD >"+rs.getInt("user_age")+"</TD>"); out.print("<TD >"+rs.getString("user_sex")+"</TD>"); out.print("<TD >"+rs.getString("user_address")+"</TD>"); out.print("<TD >"+rs.getString("user_telephone")+"</TD>"); out.print("<TD >"+rs.getString("add_time")+"</TD>");
out.print("</TR>") ;
}
out.print("</Table>"); con.close();
}
catch(SQLException e1)
{
out.print("SQL 异常!");
}
%>
</BODY>
</HTML>
上面的程序先用 Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");语句加载驱动,再用类方法 DriverManager.getConnection("jdbc:odbc:testDB");得到连接对象,用连接对象的 createStatement()方法创建 SQL 语句对象,通过语句对象来执行 SQL 查询,得到结果记录集后,用 next()方法移动记录指针从而输出所有记录。
前面的代码我们是利用 JDBC-ODBC 方式连接的,如果是 JDBC 方式的话,我们只需要改动两个地方就可以了。一个是将 Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");改成
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver"); 。 第 二 个是将
DriverManager.getConnection("jdbc:odbc:testDB"); 改 成 DriverManager.getConnection("jdbc:microsoft:sqlserver://localhost:1433;Database Name=hack", "sa", "hack");就可以实现以 JDBC 的方式来连接数据库。从返回的结果来看不管是用哪种连接方式,结果都是一样的。
数据库连接是一种重要的有限的昂贵资源,当访问网站用户越来越多时显得尤为突出。这个时候需要加强对数据库连接的管理,而且显著的影响到了整个应用程序的伸缩性和健壮性,影响到程序的性能。这个时候 Java 提供了一种叫做数据库连接池(Connection Pool)的技术,它可以明显地提高数据库操作的性能。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
对于数据库连接池技术的更多内容大家在系统性学习 JSP 编程的时候可以去具体学习,光盘中自带的 JSP 电子书就是一个很好的选择。
JavaBean和Servlet简介
(1)、JavaBean 简介
在本节为大家介绍 JavaBean 和Servlet,其实他们对我们分析 JSP 代码漏洞没有很大的直接联系,因为根本就无法看到他们里面代码。不过他们是 JSP 架构系统常用的组件,所以这里给大家讲的目的并不是为了找他们的漏洞,是为了学习他们,从而让我能够更加全面的了解一个 JSP 的架构,让我们在分析具体的 JSP 代码时有一个比较清晰和完整的思路。
JavaBean 是 Java 程序设计应用中的一种组件技术。在 JSP 中,可以引用它来完成一些复杂的工作,从而简化编程的难度。SUN 公司把 JavaBean 定义为一个可重复使用的软件组件。其实 Java 开发的 JavaBean 就是一个类,一个用面向对象编程中的封装了属性和方法的用来完成特定的某种功能的类。
编写 JavaBean 其实质上就是编写一个 Java 类,设计 JavaBean 类就是要设计这个
JavaBean 的属性和方法,类的方法的命名有以下规则:
如果成员变量的名字是 xxxx,则相应地有两个用来得到成员变量值和设置变量值的方法,它们分别是 getXxxx()和setXxxx()。即如下两种形式:
Public dataType getXxxx()
Public void setXxxx(dataType data)
其中,dataType 是成员变量的数据类型;参数 data 是给成员变量赋予的值。还要注意的是 getXxxx()和 setXxxx()中的变量名字的第一个字母为大写。还有访问成员变量的方法都设为 public。
因为 JavaBean 是一个类,所以编写的 JavaBean 的代码保存的后缀名是 java,而不是
jsp。因为 java 文件,所以首先就是要用 JDK 编译这个文件,编译后就得到 class 文件。编译 JavaBean 之后就是要对其进行部署,也就是让 JavaBean 在哪里有效,比如在整个 JSP系统中都有效或者在当前应用时有效。
部署 JavaBean 有两种方式。第一种是部署 class,就是部署我们刚才编译生成的 class文件,让它在哪个地方有效,在哪个地方无效;第二种是部署 jar,对于 jar 这里给大家解释一下,它是一个压缩包,可以包含一组类及其相关资源,把这些文件组成统一的一个文件
——JAR 文件。有了 JAR 文件不必向上面的 class 文件一样一个一个地部署,因为在一个 JAR文件中已经包含了多个 class 文件,所以可以用一个 JAR 文件做一次部署。
这里大家要明白一点,一般情况下,我们得到一套 JSP 系统后,如果其中有 JavaBean结构,我们是无法得到 JavaBean 这个类中的源代码的,因为 JavaBean 源代码被编译成了
class 文件了,如果仅仅是这样我们还是可以利用工具将 class 文件反编译得到 java 文件代码。但是目前的编译器一般都自带了一个叫混淆器的工具,class 文件经过它混淆之后我们是无法在反编译得到源代码的。
①、在 JSP 中应用 JavaBean
要在 JSP 中应用JavaBean,必须使用 JSP 指令标签 useBean 来声明 JavaBean 的实例。这个指令标签的语法如下:
<jsp:useBean id="给 JavaBean 实例取的名称" class="Bean 类名" scope="JavaBean 实例的有效范围"></jsp:useBean>
或者
<jsp:useBean id="给 JavaBean 实例取的名称" class="Bean 类名" scope="JavaBean 实例的有效范围"/>
id 的设置我们可以任意;class 为 JavaBean 的类名,如果类之上还有包,则次参数用形如“包名.类名”的形式;scope 的取值有四种,这里就不再重复了,如果还有不清楚的话,可以参考前面 12.2.4 节中对 useBean 指令的介绍。
在JSP 页面中要能使用JavaBean 还应事先在文件头部导入这个JavaBean 对应的类,如:
<%@ page import="类名"%>
如果还有包,则要要在类名前面加上包名,形如“包名.类名”。
②、设置与得到 Bean 的属性值
在前面我们介绍了使用getXxxx()和setXxxx()方法类得到和设置JavaBean 的属性值,同时我们还可以利用动作标签 getProperty 和 setProperty 分别用来获取和修改 JavaBean的属性值。对于 getProperty 和 setProperty 这两个指令,在 12.2.4 中已经做过详细的介绍,在这里就不在细说,还有不懂的话可以参考 12.2.4 节。
前面给大家讲了一个计数器的例子,下面我还用这个例子来给大家说明如何具体应用
JavaBean,其中 JavaBean 类的代码 conuter.java 文件的代码如下所示: Public class Counter
{ long count=0;
public long getCount() //建立getCount 方法用于总的访问量
{
return count;
}
public synchronized long setCount() //建立 setCount()方法用于将 count 加1
{count++; return count;
}
}
上面建立一个 Counter 的公共类,类中声明了一个成员变量 conut,这个变量用于记录
访问者的累积数字。getCount()方法得到这个数字,setCount()方法用于把数据加 1。编写好了上面的代码之后就用 JDK 编译一下,得到了 conuter.class 文件。而 JSP 文件的代码如下所示:
<%@ page contentType="text/html;charset=GB2312" %> //显示中文的 JSP 指令
<%@ page import="Counter" %> 用于包含 JavaBean 的类
<jsp:useBean id="counter" class="Counter" scope="application">
</jsp:useBean> //用于声明 JavaBean 的实例
<HTML>
<head>
<title>用 JavaBean 作网站计数器</title>
</head>
<BODY>
<% if(session.isNew()) //如果是一个新的会话
counter.setCount(); //调用 JavaBean 中counter 的 setCount()方法
%>
<P><P>欢迎访问本站,您是第
<%=counter.getCount()%> //输出总的访问量个访问用户。
</BODY>
</HTML>
上面的代码把 JavaBean 实例 counter 的范围声明为 application,表示在应用期间都有效,即从 Web 服务器开启至服务器关闭时期间有效。程序运行的结果如图 12-9 所示。
图 12-9 引用 JavaBean 后的计数器结果
(2)、Servlet 简介
Servlet 是用 Java 编写的运行在 Web 服务器中的程序。Servlet 其实就是 Java 代码用于 Web 的一个技术,要开发 Servlet 需要掌握比较多的 Java 知识,当然我们这里是不可能给大家讲解如何编写 Servlet 了,只是让大家感性的认识一下 Servlet 的各种常识,这可以让我们在分析代码的时候思路更加的清晰。
其实我们的 JSP 页面在编写完毕后在 Web 引擎中运行前也会被编译器先转换成为
Servlet,然后再编译成字节码,因此 JSP 页面与 Servlet 其实是一一对应的。一个 Servlet 的生命周期主要有三个过程:
①、Servlet 的初始化。Servlet 实际上是一个类,当第一次被客户端请求时,Web 服务器引擎首先生成这个 Servlet 类的对象,并加载这个对象,通过这个对象的 init()方法完成一些初始化的工作
②、生成的 Servlet 类的对象调用 service()方法来响应请求。
③、Servlet 类对象自第一次被生成后将常驻在内存中直到 Web 服务器被关闭,当再次请求将直接从内存中取出对象来响应请求。当 Web 服务被关闭时,将调用 Servlet 类的对象
destory()方法清除这个对象。
在上面的三个过程中,init()方法只被调用一次,即第一次被请求时调用;service()方法在每次 Servlet 被请求时都会被执行。
要进行 Servlet 的开发就需要用到 javax.Servlet 和 javax.Servlet.http。对于我们在 Web 开发,只需要用到 javax.Servlet.http 中的类就可以了。
当客户端请求 Servlet 时,服务器端将接收两个类的对象,一个是 ServletRequest,用于描述客户端对服务器端的请求;另一个是 ServletResponse,用于描述服务器端对客户端的响应。
Servlet 类从 HttpServlet 类中扩展而来。在进行 Servlet 开发时一般都会用到下面的方法:
①、init()
这个方法用于完成一些初始化的工作,如数据库连接的初始化、设置变量的值等。当 Servlet第一次被请求时调用该方法,再次请求时就不会了。调用的格式为:
init(ServletConfig config) throws ServletException
当方法中出现了异常时会抛出 ServletException 异常
②、doPost()或 doGet()
这两个方法分别用来处理 HTTP 的 post 请求和 get 请求。客户端请求数据时用 post(get)方法时, 如表单的请求 method 为 post(get) ,则接收数据的 Servlet 就会调用
doPost()(doGet())。调用他们的格式为:
doPost( 或 者 doGet)(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException
③、service()
这个方法用来处理对客户端的响应,其语法格式为:
service(HttpServletRequest request,HttpServletResponse response) throws IOException
每个客户端的请求都会被调用 service()方法,每个客户端对应的此方法是不同的,互不相干。
④、destory()
当 Web 服务关闭时就会调用 Servlet 的 destory()方法。
下面我们看看一个最简单的 Servlet,文件名为 firstServlet.java,其代码如下: import java.io.*; //引用 io 包中的所有类,在 Java 中用import 关键字来引用包 import javax.servlet.*; //引用 servlet 包中的所有类
import javax.servlet.http.*;
public class firstServlet extends HttpServlet
// firstServlet 类继承于 HttpServlet 类
{
public void init(ServletConfig config) throws ServletException
//完成Servlet 的初始化工作
{
super.init(config);
}
public void service(HttpServletRequest request,HttpServletResponse response) throws IOException
//利用service 方法来完成对客户端访问的请求的响应
{
//设置 mime response.setContentType("text/html;charset=GB2312");
//作用为用于设置显示中文
PrintWriter out=response.getWriter(); out.println("<HTML> <BODY>");
out.println("这是一个简单的 servlet。");
out.println("客户端 IP 地址是:"+request.getRemoteAddr()+"<br>"); out.println("</body> </html>");
}
}
程序中的 init()方法直接继承与 HttpServlet 类中的init()方法,用 setContentType()
设置 mime 类型,用于显示中文,用 HttpServletResponse 类对象 response 的 getWriter()来得到输出流,用 HttpServletRequest 类对象 request 的 getRemoteAddr()方法得到客户
端的 IP 地址。
上面的代码我们保存为 firstServlet.java 后,用 JDK 编译成 firstServlet.class 文件,这个过程就是编译 servlet。和 JavaBean 一样,在编译完了之后就要部署 servlet。部署 servlet 很简单,有两种方法,一是将字节码文件.class 拷贝到 Tomcat 的安装目录
classes 的子目录下,这样这个 servlet 在所有的应用中都可以使用;第二种是将字节码文件.class 拷贝到当前应用的 WEB-INF/classes 子目录下,这样这个 servlet 仅在当前的应用中有效。这里我们把firstServlet.class 拷贝到Tomcat 的安装目录classes 的子目录下,然后在浏览器中输入 http://localhost/servlet/firstServlet,即运行 Servlet,运行的结果如图 12-10 所示。
图 12-10
在本节中简单的给大家介绍了一下 JavaBean 和 Servlet,相信大家已经对他们有了一定的认识了。在我们实际的 JSP 系统中,常常会用他们一起来搭配开发系统,比如 jsp+servlet。现在大家都知道了 JavaBean 和Servlet 都是要经过编译后才运行的,所以在正常的情况下如果一个系统中采用了这两种技术的话,一般我们是看不到代码的。虽然现在有了对这些 class 进行反编译的工具,不过不要忘记了,大部分搞 JAVA 开发的程序员都知道用混淆器将代码进行混淆,所以一般情况下是无法得到 JavaBean 和Servlet 的源代码的。当然我们在进行代码分析的时候还是要试一下对其进行反编译,如果这个系统没有采用混淆器进行混淆的话,那我想开发这个系统的程序员也太没有安全意识了,其系统可能就根本没有任何的安全措施,我们可以轻易的攻破。
到这里,我们的 JSP 基础就给大家讲完了。在本章中,有一些基础知识我没有提到,这是因为在前面的学习过程中我们已经学过了,所以这里不再重复。如果有什么基础不懂的话可以再去看前面几章的基础知识,从书的前到后他们都是连在一起的,所以大家一定要跟上我的思路。还有一点就是本章中还涉及到一定的 Java 基础知识,如果有什么地方还理解不透的话,可以去看一下 Java 方面的书籍,在光盘中我已经为大家收录了。
对于 JSP 的知识,我这里也只提及了一些与黑客技术紧密相关的。还有很多是没有讲到。所以要进行 JSP 系统开发的话,还有很多东西要学。JSP 系统不同于前面的 ASP 和 PHP,用它来做的系统可以非常的复杂,比如构建一个 J2EE 系统等等。而且由于 JSP 系统的特殊性,很多地方我们是得不到源代码,这样就加大了我们分析代码漏洞的难度。
如果一个 JSP 系统的开发仅仅是 JSP 的知识的话,那么它和 ASP 及PHP 就没什么很大区别,对于这样的系统我们可以轻易的找出其漏洞,不过目前仅仅利用 JSP 的知识来开发的系统比较少。大部分系统都是联合开发,例如 JSP+JavaBean 或者 JSP+Servlet。当然,这些并不能阻止我们对他们进行漏洞的挖掘,而且目前国内对 JSP 系统漏洞方面的挖掘还是比较少,有很大的发挥空间。相信随着 JSP 流行会有越来越多的系统被开发出来,而我们的任务就是要挖掘他们的漏洞。在下一章我将用实例为大家讲述目前在 JSP 系统中常见的安全漏
洞,一定很期待吧!
声明:本章中部分 JSP 的基础知识参考了由电子工业出版社出版的《JSP 网络编程》,该书由邓子云、张赐编著。
你从本章可以学到如下几点:
1、JSP 源代码泄露漏洞分析、查找及利用
2、跨站脚本漏洞分析
3、注入漏洞代码分析
4、JSP 安全编程技术简介
5、JSP 木马编写技术
6、其他的安全问题