1.2 热点关键技术
1.2.1 防SQL注入技术
SQL注入是指一些精通SQL语句的用户,通过在表单或浏览器地址栏中输入SQL语句来绕过系统检验的一种技术。一般防范SQL注入的手段是过滤敏感字符,例如引号等。
PHP相对于ASP来说,要安全得多。但这不代表PHP网站就不会出现SQL注入的情况。幸运的是,在PHP上防SQL注入,要比ASP简单、方便,不需要写一大段的转换语句,只要几个函数就可以了。
1.intval()函数
intval()函数的作用是返回变量的整数值,函数语法如下:
int intval ( mixed var [, int base] )
2.addslashes()函数
addslashes()函数就是在操作数据库时,对其中的特殊字符进行自动转义,即在特殊字符前加上反斜线(\),包括单引号(' )、双引号(")、NULL,但是不包括“%”和“_”。函数语法如下:
string addslashes ( string str )
此外,还可以使用mysql_real_escape_string()来进行转义,效果与addslashes()函数一样。
1.2.2 Ajax无刷新验证技术
Ajax(Asynchronous JavaScript and XML)即异步JavaScript和XML,是时下最流行的技术。Ajax不是新的技术,而是原有技术的集合,这从它的名字上就能够看出来。
Ajax的核心技术是XMLHttpRequest。通过XMLHttpRequest中的open()方法和send()方法,可以在不刷新当前页面的情况下向处理页发送数据;通过XMLHttpRequest中的responseText属性和responseXML属性,可以得到处理页的输出结果。
Ajax能够流行的原因,是因为它能为用户提供良好的交互性,这个特性在本模块中被充分地体现出来。注册用户无须经过“漫长”的等待就可以知道输入的账号是否可用,登录用户也不必担心因为输错登录信息而重新刷新整个页面。
使用Ajax,一般分为下面几步。
(1)创建XMLHttpRequest对象。不同的浏览器,创建XMLHttpRequest对象及使用的方法有一些差别,这里只针对IE浏览器进行创建,代码如下:
var xmlhttp = false; //初始化变量 //如果ActiveXObject存在,说明是IE 1.0以上的版本,否则使用XMLHttpRequest创建 if(window.ActiveXObject){ xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }else if(window.XMLHttpReuqest){ xmlhttp = new XMLHttpRequest(); }
(2)对象创建成功后,就可以使用对象中的open()方法创建新请求了,方法格式如下:
xmlhttp.open(rmethod, rurl, isAsync);
参数说明如下。
❑ rmethod:指明请求的方法,如get()或post()。
❑ rurl:指明请求的页面。可以是绝对地址,也可以是相对地址。
❑ isAsync:指明请求是否为异步。默认为True,即异步。
(3)如果isAsync等于True,那么当请求的状态改变时,将调用onreadystatechange属性,该属性指定了一个回调函数,格式如下:
xmlhttp.onreadystatechange = reabackfunc;
或者
xmlhttp.onreadystatechange = function(){…}
(4)在回调函数中,首先需要判断http的请求状态和http状态码。这是通过readyState属性和status属性来判断的。readyState属性有5种状态值,常用的是4,表示数据接收完毕。status属性的值比较多,常用的是200,表示请求成功。一般使用这两个属性一起来判断,格式如下:
xmlhttp.onreadystatechange = function(){ if(readyState == 4 and status == 200){ … } }
(5)当响应页处理结束后,即满足了“readystate==4 and status == 200”这个条件,就可以使用XMLHttpRequest对象的属性获取响应页的值了。常用的有responseText、responesXML、responseStream等,这里以responseText为例进行介绍。
responesText属性是将响应页的输出信息作为字符串返回,格式如下:
str = xmlhttp.responseText;
(6)最后使用send()方法来接收回应。send()方法可以传递数据,但这取决于open()方法中的rmethod参数,当参数为get时,数据是附在URL中进行传递的;当参数为post时,数据只能使用send()方法进行传递。send()方法的语法如下:
xmlhttp.send([rdate]);
1.2.3 验证码技术
验证码技术是为了防止用户名和密码被暴力破解,在登录页面上生成一组随机数,每次刷新页面时,随机数都会改变。一般是4位,也有更多位的。
1.获取验证码
PHP中的验证码可以通过rand()函数生成随机数的方式得到。rand()函数可以获取指定范围内的随机数,该函数语法如下:
int rand([int min, int max])
如果省略两个参数,那么将返回0到RAND_MAX之间的随机整数。否则,返回min和max之间的整数。例如本例中要获取4位十六进制的整数,代码如下:
<? php for($i=0; $i<4; $i++){ $num .= dechex(rand(0,15)); //生成随机数 } … ?>
函数dechex()可以将参数转换为十六进制数表示。
使用JavaScript也可以生成十六进制随机数,但是稍有些复杂。JavaScript中不能直接将十进制数转换为十六进制数,需要手动进行转换。首先使用Math.random()函数生成0~15的随机数,然后使用Math.ceil()函数将随机数取整,接下来就要逐次判断该值,如果该值大于9,那么将10~15的数一一对应转换为a、b、…、f。转换完成后,将值累加,最后传给valcode.php页。
使用JavaScript生成十六进制随机数的完整代码如下:
//生成随机数 function showval(){ num = ' ' ; for(i=0; i<4; i++){ //循环输出4位验证码 tmp = Math.ceil((Math.random() * 15)); //取得1位十六进制的整数 if(tmp > 9){ //依次判断随机数 switch(tmp){ case(10): //如果随机数等于10,则转换为a num += ' a' ; break; case(11): num += ' b' ; //如果随机数等于11,则转换为b break; case(12): num += ' c' ; //如果随机数等于12,则转换为c break; case(13): //如果随机数等于13,则转换为d num += ' d' ; break; case(14): //如果随机数等于14,则转换为e num += ' e' ; break; case(15): //如果随机数等于15,则转换为f num += ' f' ; break; } }else{ num += tmp; } } $(' chkid' ).src=' valcode.php? num=' +num; //将生成的随机数传给图像生成页 $(' chknm' ).value = num; //将随机数的值保存到页面的隐藏域中 }
2.显示随机数图片
显示随机数的方法很多,将随机数写入一个图片中再显示是目前常用的方法。在PHP中,可以使用GD函数库来实现。使用到的函数主要有imagecreate()函数、imagecolorallocate()函数、imagestring()函数、imagesetpixel()函数、imagepng()函数和imagedestroy()函数。
1)imagecreate()函数
imagecreate()函数用来创建一个基于调色板的空白图像源,这是生成图片的第一步,函数语法如下:
resource imagecreate ( int width, int height )
其中,参数width和height分别指定了图像的宽和高。
2)imagecolorallocate()函数
imagecolorallocate()函数可以为创建后的图像分配颜色,函数语法如下:
int imagecolorallocate ( resource image, int red, int green, int blue )
参数说明如下。
❑ image:一个图像源。
❑ red、green和blue:表示红、黄、蓝三元素的成分,每种颜色的取值范围在1~255。
3)imagestring()函数
图像创建完成后,就可以使用imagestring()函数来添加图像文字了,该函数的语法如下:
bool imagestring ( resource image, int font, int x, int y, string s, int col )
参数说明如下。
❑ image:一个图像源。
❑ font:可以设置字体,如果使用系统默认字体,则可以使用1~5的数字。
❑ x和y:分别表示文字相对于整幅图像的x轴和y轴坐标,即所输入的字符串的左上角坐标。
❑ s:要显示的字符串。
❑ col:为字体颜色,也是使用imagecolorallocate()函数来分配的。
4)imagesetpixel()函数
使用imagecolorallocate()创建的是一个单一背景色的图像,如果希望向图像中添加干扰码,则可以使用imagesetpixel()函数,该函数的作用是画一个像素点,函数语法如下:
bool imagesetpixel ( resource image, int x, int y, int color )
参数说明与imagestring()函数相似,这里不再赘述。
5)imagepng()函数
该函数将创建完成的图片以png的格式输出,函数语法如下:
bool imagepng ( resource image [, string filename] )
参数说明如下。
❑ image:要保存的图像源。
❑ filename:要保存的图像名。如果省略,则直接输出到浏览器。
6)imagedestroy()函数
图像保存完毕后,使用imagedestroy()函数来释放内存,函数语法如下:
bool imagedestroy ( resource image )
验证码的生成文件是valcode.php,其完整代码如下:
<? php header("content-type:image/png"); //设置页面编码 $num = $_GET[' num' ]; //获取超链接传递的随机数 $imagewidth=60; //定义画布的宽 $imageheight=18; //定义画布的高 $numimage = imagecreate($imagewidth, $imageheight); //创建画布 imagecolorallocate($numimage,240,240,240); //设置画布颜色 for($i=0; $i<strlen($num); $i++){ //循环读取随机数 $x = mt_rand(1,8)+$imagewidth*$i/4; $y = mt_rand(1, $imageheight/4); $color=imagecolorallocate($numimage, mt_rand(0,150), mt_rand(0,150), mt_rand (0,150)); //定义图像的颜色 imagestring($numimage,5, $x, $y, $num[$i], $color); //将随机数写入画布中 } for($i=0; $i<200; $i++){ //for循环语句生成干扰线 $randcolor=imagecolorallocate($numimage, rand(200,255), rand(200,255), rand (200,255)); //定义颜色 //生成干扰线 imagesetpixel($numimage, rand()%70, rand()%20, $randcolor); } imagepng($numimage); //生成图像 imagedestroy($numimage); //释放资源 ?>
1.2.4 E-mail激活技术
为了防止恶意注册,现在有很多的网站采用了E-mail激活技术。当用户注册成功后,并不是马上就可以使用,而是需要先登录邮箱。通过系统发出的E-mail上的链接进行激活,只有激活后,账号才可以使用。对于找回密码的用户,系统会将新密码发送到邮箱中,这在一定程度上,提高了安全性。
本模块使用ZendFramework中的Zend_Mail组件完成邮件的发送操作,在本书的光盘中没有提供Zend_Mail组件,如果要运行这个程序,需要读者自己下载Zend组件,并将其复制到本模块的根目录下,即mr/04文件夹下。
实践真知 说明
在执行邮件的发送时,笔者使用的是搜狐邮箱,邮箱名称是mrsoft8888@sohu.com,密码是mrsoft8888。读者在完成用户注册之后,可以登录这个邮箱完成注册用户的激活操作,否则注册的用户是不能够使用的。
注册用户激活的原理是控制数据库中用户信息表(tb_member)中active字段的值,默认值注册成功后该字段的值是0,但是此时该用户是不能够登录网站的,必须通过邮箱激活,将该字段的值更新为1之后,此用户才可以使用。激活操作的文件存储于activation.php文件中,其关键代码如下:
<? php session_start(); header(' Content-Type:text/html; charset=gb2312' ); include_once("conn/conn.php"); if (! empty($_GET[' name' ]) && ! is_null($_GET[' name' ])){ //激活注册用户 $num=$conne->getRowsNum("select * from tb_member where name =' ".$_GET[' name' ]."' and password = ' ".$_ GET[' pwd' ]."' "); if ($num>0){ $upnum=$conne->uidRst("update tb_member set active = 1 where name= '".$_GET[' name' ]."' and password = ' ".$_GET[' pwd' ]."' "); if($upnum > 0){ $_SESSION[' name' ] = $_GET[' name' ]; echo "<script>alert(’用户激活成功!' ); window.location. href=' main.php' ; </script>"; }else{ echo "<script>alert(’您已经激活!' ); window.location.href=' main.php' ; </script>"; } }else{ echo "<script>alert(’用户激活失败!' ); window.location.href= 'register.php' ; </script>"; } } ?>
1.2.5 应用键盘响应事件验证信息是否合法
新用户注册时,随着信息输入,系统实时显示信息的正确性;用户登录时,无须使用鼠标来选择文本框,只要按<Enter>键,焦点就自动下移。这些功能都是通过JavaScript脚本中的键盘响应事件来实现的。随着Ajax技术的流行,JavaScript已经成为一个开发人员必须要掌握的技术。下面,就来简单了解键盘事件。
用户通过onkeydown和onkeyup事件来触发响应事件。使用方法与onclick事件类似。onkeydown表示当键盘上的键被按下时触发,onkeyup和它正好相反,当键盘上的键被按下又抬起时触发。在页面中加载事件的方式有多种,这里介绍两种最常用的方式。
1.将事件直接添加到页面元素中
方法最直接、简单,格式如下:
<script type="text/javascript"> … function refer(){ … } </script> <input type="text" onkeydown="refer()" />
当该用户输入完信息后,单击任意键,onkeydown事件被触发,并调用refer()函数。
2.通过window.onload加载
当页面被载入时,事件被载入,格式如下:
<script> window.onload = function(){ document.getElementById(' lgname' ).onkeydown = function(){ … }` } </script> … <input id="lgname" type="text" /> …
当用户输入信息时,每输入一个字母,都将触发该事件,在该事件调用的函数中,对用户输入信息进行判断。例如用户名必须大于等于2、密码最短6位、E-mail必须合法等。下面看一段验证用户名的代码:
//验证用户名 //为id等于regname的页面元素添加onkeyup事件 //用户每按一次键,都会调用一次该函数 $(' regname' ).onkeyup = function (){ name = $(' regname' ).value; //获取输入内容 cname2 = ' ' ; if(name.match(/^[a-zA-Z_]*/) == ' ' ){ //判断输入字符是否在有效范围之内 $(' namediv' ).innerHTML = ' <font color=red>必须以字母或下画线开头</font>' ; cname1 = ' ' ; }else if(name.length < 2){ //判断输入的字符长度 $(' namediv' ).innerHTML = ' <font color=red>注册名称必须大于等于2位</font>' ; cname1 = ' ' ; }else{ $(' namediv' ).innerHTML = ' <font color=green>注册名称符合标准</font>' ; cname1 = ' yes' ; } chkreg(); } …
该段代码的运行结果如图1.6所示。
图1.6 验证信息合法性
使用onkeydown事件还可以实现对特定键的控制,包括<Enter>键、<Ctrl>键、<Alt>键等所有的按键,这是通过在onkeydown事件中使用keyCode属性来实现的。keyCode属性能够知道用户按下的是哪个键,例如<Enter>键等于13、空格键等于32等。使用keyCode属性的一般格式如下:
<script> window.onload = function(){ document.getElementById(' lgname' ) = function(){ if(event.keyCode == 13){ //判断用户是否按了<Enter>键 … } }` } </script>
在本模块中,实现了焦点自动下移功能。当用户按<Enter>键时,页面的焦点自动移到下一个文本框中,实现后的效果如图1.7所示。
图1.7 焦点自动下移的运行效果
1.2.6 PHP中操作Cookie技术
Cookie的作用是当用户第一次访问某服务器时,服务器将一些信息保存到客户端计算机内。在以后的一段时间内,当用户再次访问这个服务器时,服务器通过Cookie信息,能够识别该用户。在很长一段时间内,Cookie都被当做一个不安全的因素,很少再被使用,转而使用Session。但是,Cookie的功能确实十分方便,自动登录、网站统计等都比其他的实现方式要简单、快捷得多。Cookie只是一个文本文件,不能访问本地硬盘,无法传播病毒木马程序。唯一要注意的地方是,Cookie只能识别计算机,而不会在意谁在使用。
在PHP中操作Cookie使用setcookie()函数和$_COOKIE预定义变量。setcookie()函数的语法格式如下:
bool setcookie ( string name [, string value [, int expire [, string path [, string domain [, bool secure]]]]] )
参数说明如下。
❑ name:设置了Cookie的名字。
❑ value:Cookie的值,即参数name的值。
❑ expire:用来设置Cookie的过期时间,该参数以时间戳的形式存在。一般设置Cookie过期时间的时候,通过time()+秒数来实现,如time()+60×10,表示Cookie将在10分钟后失效。设置Cookie马上失效,可以将时间设为当前日期之前,如time()-1,那么Cookie会立即失效。
❑ path:表示Cookie在服务器端的有效范围。如果path设为“/”,那么Cookie在整个服务器内都有效,如果Cookie为“/05/”,那么Cookie只在服务器下的05目录有效。
❑ domain:指定了Cookie有效的域名范围,以www.mrbccd.com为例,如果domain设为“.mrbccd.com”,那么Cookie在该域的所有子域都有效;如果domain为“www.mrbccd.com”,那么Cookie只在www.mrbccd.com域内有效。
❑ secure:表明是否在https传送。如果是True, Cookie仅在https连接中被设置,默认是False。
1.2.7 在JavaScript中操作Cookie技术
在JavaScript中可以通过document对象中的Cookie属性对Cookie进行创建、读取、删除等操作。先来看创建Cookie的格式(代码中加粗的部分是需要用户设置的)。
documen.cookie = "Name=Value; expires=time; path=URL; domain= Domain"
其中各个参数的含义与PHP中的基本相同,这里不再赘述。
在JavaScript中读取Cookie有一点复杂。在JavaScript看来,Cookie就是一个字符串。例如一个名字为“Count”、值是“10”的Cookie,在JavaScript中的格式是
count=10
如果有多个Cookie,那么它的格式如下:
count=10; name=mr; …
而真正需要的值,其实只有10而已。所以,要获取Cookie的值,就要使用split()函数将Cookie进行拆分,获取count值的代码为
<script> if(document.cookie==' ' ){ //Cookie等于空时 document.write(' no cookie' ); }else{ //以分号为分隔符,对Cookie进行分隔 cookiearr = document.cookie.split(' ; ' ); leng = cookiearr.length; //获取数组长度 for(i=0; i<leng; i++){ //判断哪一个Cookie等于count if(cookiearr[i].split(' =' )[0] == ' count' ){ //输出count的值 document.write(cookiearr[i].split(' =' )[1]); } } } </script>
1.2.8 用户自动登录技术
自动登录的原理是:当用户打开登录页面时,登录页面首先判断用户客户端机器中的Cookie值。如果该值不存在,或者Cookie已失效,这时,将显示登录表单;如果Cookie值存在,则直接使用Cookie中所保存的用户名进行登录。
在本模块中,用户首先打开的是index.php页,该页判断Cookie值,如果没有,则跳转到login.php进行登录;如果Cookie有值,则将Cookie中的用户名保存到Session,直接进入主页面,实现代码如下:
<? php session_start(); //开启Session支持 header(' Content-Type:text/html; charset=gb2312' ); //设置页面编码 //判断Cookie是否为空 if(! empty($_COOKIE[' name' ]) and ! is_null($_COOKIE[' name' ])){ $_SESSION[' name' ] = $_COOKIE[' name' ]; //将Cookie保存到Session中 //跳转到main.php页 header(' location:http://localhost/model/05/01/main.php' ); }else{ //Cookie为空,说明没有登录 //跳转到login.php页 header(' location:http://localhost/model/05/01/login.php' ); } ?>
这里$_COOKIE[' name' ]的值是在登录成功时设置的,相关代码如下:
<? php … //如果登录成功 setcookie(' name' , $name, time()+60*10); //保存$name的同时设置时间 … ?>