|
|
|
|
目前,不少网站为了防止用户利用机器人自动注册、登录、灌水,都采用了验证码技术。所谓验证码,就是将一串随机产生的数字或符号,生成一幅图片,图片里加上一些干扰象素(防止OCR),由用户肉眼识别其中的验证码信息,输入表单提交网站验证,验证成功后才能使用某项功能。 PHP实现: 我们这里展示了如何编写PHP程序实现验证码功能: 代码一: <?php /* * Filename: authpage.php * Author: hutuworm * Date: 2003-04-28 * @Copyleft hutuworm.org */ srand((double)microtime()*1000000); //验证用户输入是否和验证码一致 if(isset($HTTP_POST_VARS['authinput'])) { if(strcmp($HTTP_POST_VARS['authnum'],$HTTP_POST_VARS['authinput'])==0) echo "验证成功!"; else echo "验证失败!"; } //生成新的四位整数验证码 while(($authnum=rand()%10000)<1000); ?> <form action=authpage.php method=post> <table> 请输入验证码:<input type=text name=authinput style="width: 80px"><br> <input type=submit name="验证" value="提交验证码"> <input type=hidden name=authnum value=<? echo $authnum; ?>> <img src=authimg.php?authnum=<? echo $authnum; ?>> </table> </form> 代码二: <?php /* * Filename: authimg.php * Author: hutuworm * Date: 2003-04-28 * @Copyleft hutuworm.org */ //生成验证码图片 Header("Content-type: image/PNG"); srand((double)microtime()*1000000); $im = imagecreate(58,28); $black = ImageColorAllocate($im, 0,0,0); $white = ImageColorAllocate($im, 255,255,255); $gray = ImageColorAllocate($im, 200,200,200); imagefill($im,68,30,$gray); //将四位整数验证码绘入图片 imagestring($im, 5, 10, 8, $HTTP_GET_VARS['authnum'], $black); for($i=0;$i<50;$i++) //加入干扰象素 { imagesetpixel($im, rand()%70 , rand()%30 , $black); } ImagePNG($im); ImageDestroy($im); ?> 本文程序在Apache 2.0.45 + PHP 4.3.1环境下运行通过。 上文只是对验证码功能的一个简单实现,并没有考虑商用安全性问题。如果要增强安全性,将此功能投入商业应用,则可以通过以下几个步骤实现: 1. 启用Session。 2. authnum在authimg.php中生成,并计算md5sum,存入session。 3. authpage.php将authinput计算md5sum后,与session中的authnum(md5sum)对比得出验证结果。 本站注:作者使用了简单的代码实现了很酷的功能。不过在添加干扰像素时的效果不是太好,大家可以看一下雨声论坛登录时的效验码(http://ror.cn/perl/ut/user_login.cgi),偶把第二段代码稍改了一下,生成了与其类似的效果。 修改后的代码如下: <?php /* * Filename: authimg.php * Author: hutuworm * Date: 2003-04-28 * @Copyleft hutuworm.org */ //生成验证码图片 Header("Content-type: image/PNG"); srand((double)microtime()*1000000); $im = imagecreate(62,20); $black = ImageColorAllocate($im, 0,0,0); $white = ImageColorAllocate($im, 255,255,255); $gray = ImageColorAllocate($im, 200,200,200); imagefill($im,68,30,$gray); while(($authnum=rand()%100000)<10000); //将四位整数验证码绘入图片 imagestring($im, 5, 10, 3, $authnum, $black); for($i=0;$i<200;$i++) //加入干扰象素 { $randcolor = ImageColorallocate($im,rand(0,255),rand(0,255),rand(0,255)); imagesetpixel($im, rand()%70 , rand()%30 , $randcolor); } ImagePNG($im); ImageDestroy($im); ?> 显示结果如下图: [在新窗口中浏览该图片] ![]() 有兴趣的朋友可以自己试一下。 看一下上面这段代码: 第一次看PHP代码,呵呵。奇怪的学习经历。 代码一: <?php /* * Filename: authpage.php * Author: hutuworm * Date: 2003-04-28 * @Copyleft hutuworm.org */ //以系统时间为种子得到随机数 //srand -- 播下随机数发生器种子 //microtime -- 返回当前 UNIX 时间戳和微秒数 srand((double)microtime()*1000000); //验证用户输入是否和验证码一致 if(isset($HTTP_POST_VARS['authinput'])) { //authnum的值是不应该发送到客户端的,这里应该是为了构建一个简单的例子先吧 //authnum 保存在 session 里应该是个可行的办法 if(strcmp($HTTP_POST_VARS['authnum'],$HTTP_POST_VARS['authinput'])==0) echo "验证成功!"; else echo "验证失败!"; } //生成新的四位整数验证码 while(($authnum=rand()%10000)<1000); ?> <form action=authpage.php method=post> <table> 请输入验证码:<input type=text name=authinput style="width: 80px"><br> <input type=submit name="验证" value="提交验证码"> <input type=hidden name=authnum value=<? echo $authnum; ?>> <img src=authimg.php?authnum=<? echo $authnum; ?>> </table> </form> 代码二: <?php /* * Filename: authimg.php * Author: hutuworm * Date: 2003-04-28 * @Copyleft hutuworm.org */ //下面几行代码肯定会让写ASP的GGDD们羡慕不已。 //生成验证码图片 //指定头信息 Header("Content-type: image/PNG"); srand((double)microtime()*1000000); //返回一个图像标识符,代表了一幅大小为58 X 28的空白图像。 $im = imagecreate(58,28); $black = ImageColorAllocate($im, 0,0,0); $white = ImageColorAllocate($im, 255,255,255); $gray = ImageColorAllocate($im, 200,200,200); imagefill($im,68,30,$gray); //强悍!! //将四位整数验证码绘入图片 //方便起见,使可以通过authimg.php?authnum=XXXX的方式得到图片。实际应用中这种做法肯定是不足取的。 imagestring($im, 5, 10, 8, $HTTP_GET_VARS['authnum'], $black); for($i=0;$i<50;$i++) //加入干扰象素 { imagesetpixel($im, rand()%70 , rand()%30 , $black); } ImagePNG($im); //imagepng -- 以 PNG 格式将图像输出到浏览器或文件 ImageDestroy($im); ?> 本文程序在Apache 2.0.45 + PHP 4.3.1环境下运行通过。 上文只是对验证码功能的一个简单实现,并没有考虑商用安全性问题。如果要增强安全性,将此功能投入商业应用,则可以通过以下几个步骤实现: 1. 启用Session。 2. authnum在authimg.php中生成,并计算md5sum,存入session。 3. authpage.php将authinput计算md5sum后,与session中的authnum(md5sum)对比得出验证结果。 本站注:作者使用了简单的代码实现了很酷的功能。不过在添加干扰像素时的效果不是太好,大家可以看一下雨声论坛登录时的效验码(http://ror.cn/perl/ut/user_login.cgi),偶把第二段代码稍改了一下,生成了与其类似的效果。 修改后的代码如下: <?php /* * Filename: authimg.php * Author: hutuworm * Date: 2003-04-28 * @Copyleft hutuworm.org */ //生成验证码图片 Header("Content-type: image/PNG"); srand((double)microtime()*1000000); $im = imagecreate(62,20); $black = ImageColorAllocate($im, 0,0,0); $white = ImageColorAllocate($im, 255,255,255); $gray = ImageColorAllocate($im, 200,200,200); imagefill($im,68,30,$gray); while(($authnum=rand()%100000)<10000); //将四位整数验证码绘入图片 imagestring($im, 5, 10, 3, $authnum, $black); for($i=0;$i<200;$i++) //加入干扰象素 { $randcolor = ImageColorallocate($im,rand(0,255),rand(0,255),rand(0,255)); imagesetpixel($im, rand()%70 , rand()%30 , $randcolor); } ImagePNG($im); ImageDestroy($im); ?> 验证码技术核心内容是动态生成图片。ASP做到这一点,最简单也是最普遍的就是用到XBM。我们先了解一下XBM的相关知识。 [转]用XBM创建动态客户端图像 作者: BUILDER.COM Wednesday, April 9 2003 11:23 AM X-Bitmap(XBM)是一种古老但通用的图像文件格式,它与现在的许多Web浏览器都兼容。X-Windows图形界面(UNIX和Linux常用的GUI)的C代码库xlib中有一个组件专门描述了它的规范。我将解释XBM格式的工作原理,然后向你展示一种更有趣的使用它的方法:在客户端创建动态图像。文章中的代码可以在此下载。 XBM基础 XBM格式本来是为存储单色的系统位图而设计的,比如图标和鼠标指针。XBM图形的实质上是使用16进制数组来表示二进制图像的C源代码文件。(16进制数组表示的二进制图像) 这里你也许会问:这种文件格式与Web浏览器有什么关系?在上世纪九十年代早期,美国超级计算应用中心(NCSA)在伊利诺斯大学开发第一个被广泛使用的Web浏览器,名为Mosaic。这个浏览器的图形支持来自很多开放源码代码库,其中就包括xlib。因此,导致今天的许多浏览器能够处理XBM图形。 Mosaic项目后来成为了Netscape浏览器的开发基础。微软也借用了一部分Mosaic代码来创建Internet Explorer。微软继而在网络信息服务器(IIS)中将XBM作为一个MIME类型注册而提供本地支持,并且在现有所有版本的Internet Explorer中将其作为一种可支持的图像。(浏览器的广泛支持) 从一个程序员的角度来看,JPEG或GIF与XBM有着极大的不同。这两种文件格式都在位级别上操作并使用了压缩算法。它们可以支持很大的颜色深度范围。创建这些动态Web图形的唯一方法是使用服务器端的脚本,比如<a href=http://stein.cshl.org/WWW/software/GD/GD.pm和CGI/Perl脚本的结合,或者通过System.Drawing名字空间访问ASP.NET中的图形设计接口类库(GDI+)。(升级到ASP.NET有很多个理由。) XBM创建起来很有程序性。每个位都被一一指定,而结果图形被限制为两色(黑色和白色)。(局限性之一:让人不爽)X-Bitmap并不是必须服务器端脚本,可以在客户端用JavaScript实时创建它们。(增加了不少灵活性) X-Bitmap的实用程序包括动态生成的图、页面计数器、老式图形图标、以及统计图表。(就是通过位控制太麻烦了,有良好的包装就好了。另外:生成图片的大小,算法的强度,是否都达到了可用的要求?)给我印象最深的XMB应用是一个叫Wolfenstein 5k的游戏,它是一个纹理映射的第一视角射击游戏,用JavaScript编写,只有5KB大小。(不错啊,稍后爽一下) 通过使用IMG标识可以很容易地将XBM文件嵌入到一个Web页面中。其语法如下: <img src=”xbmsmill.xbm”> ---------------------------------------------------------------------- 注意 这种格式不对Mac或相应的浏览器有效,比如Mozilla的早期版本。 ---------------------------------------------------------------------- 典型的XBM源代码与列表A中显示的比较相像。 #define语句以像素点为单位设置了图像的宽度和高度。你也可以使用x_hot和y_hot命令来定义图像中的一个热点。我已经创建了一个X-Bitmap来描绘这个过程。为了设计它,我先将图像映射为二进制值,如果你仔细的看,在这里你会看到一张笑脸。 我所创建的二进制图像宽16个数字高7个数字,在我们的源代码的XBM头中定义了相同大小的宽/高像素值。图像本身被存储在一个静态数组中,它包含一列二进制编码的十六进制(BCH)值——换句话说,每四位分成一组。 计算出笑脸的十六进制值得最简单的方法是一次检验图像的一行,将二进制值分成四位一组的分段,将每个分段映射成二进制/十六进制表中对应的十六进制数。下面是第一行: 0001100001100000 下面两行分别表示四位的分段和其在表A中对应的十六进制数 二进制: 0001 1000 0110 0000 十六进制: 8 1 6 0 表A XBM二/十六进制转换表 二进制 十六进制 0000 0 1000 1 0100 2 1100 3 0010 4 1010 5 0110 6 1110 7 0001 8 1001 9 0101 A 1101 B 0011 C 1011 D 0111 E 1111 F 请注意这些并不是标准的二/十六进制转换。它们是反向计算的(从左到右)而不是从右到左。把它颠倒是因为浏览器会从左到右来读图形,我们的代码必须与其一致。 最后要把这些十六进制值转换成XBM的右格式。必须在每个十六进制值前加“0x”。这是标准C++表示十六进制的方法。然后将这些值从右到左输出(每个十六进制对表示八位二进制位)。请看列表B中的例子。 因此,我们可以说: 0001100001100000=0x18,0x06 这些符合XBM的值可以很容易地插入到图像数组中: static unsigned char xbmsmile_bits[]= 现在我们来看看格式本身,现在是时候来学习在客户端浏览器中动态创建X-Bitmap了。 图片用点阵的形式表示,比如2: 00111100 0011为3 1100为C 即0x3c 01100110 0110为6 0110为6 0x66 01100000 ....... 0x表示十六进制数。 01100000 依此类推 00110000 这是用二进制数得到的点阵,其中的1为显示一黑点,0不显示 00011000 是一个反着看的2 00001100 其余数字可自已排列点阵再二进制化为十六进制数 00000110 缺点是只有黑白两种颜色 00000110 显示出来是白底黑字,要显示黑底白字的话,对其取反就行了 01111110 下面是我"画"的0-9的数字 num.asp <% Dim a(10,10) a(0,1) = "0x3c" '数字0 a(0,2) = "0x66" a(0,3) = "0xc3" a(0,4) = "0xc3" a(0,5) = "0xc3" a(0,6) = "0xc3" a(0,7) = "0xc3" a(0,8) = "0xc3" a(0,9) = "0x66" a(0,10)= "0x3c" a(1,1) = "0x18" '数字1 a(1,2) = "0x1c" a(1,3) = "0x18" a(1,4) = "0x18" a(1,5) = "0x18" a(1,6) = "0x18" a(1,7) = "0x18" a(1,8) = "0x18" a(1,9) = "0x18" a(0,10)= "0x7e" a(2,1) = "0x3c" '数字2 a(2,2) = "0x66" a(2,3) = "0x60" a(2,4) = "0x60" a(2,5) = "0x30" a(2,6) = "0x18" a(2,7) = "0x0c" a(2,8) = "0x06" a(2,9) = "0x06" a(2,10)= "0x7e" a(3,1) = "0x3c" '数字3 a(3,2) = "0x66" a(3,3) = "0xc0" a(3,4) = "0x60" a(3,5) = "0x1c" a(3,6) = "0x60" a(3,7) = "0xc0" a(3,8) = "0xc0" a(3,9) = "0x66" a(3,10)= "0x38" a(4,1) = "0x38" '数字4 a(4,2) = "0x3c" a(4,3) = "0x36" a(4,4) = "0x33" a(4,5) = "0x33" a(4,6) = "0x33" a(4,7) = "0xff" a(4,8) = "0x30" a(4,9) = "0x30" a(4,10)= "0xfe" a(5,1) = "0xfe" '数字5 a(5,2) = "0xfe" a(5,3) = "0x06" a(5,4) = "0x06" a(5,5) = "0x3e" a(5,6) = "0x60" a(5,7) = "0xc0" a(5,8) = "0xc3" a(5,9) = "0x66" a(5,10)= "0x3c" a(6,1) = "0x60" '数字6 a(6,2) = "0x30" a(6,3) = "0x18" a(6,4) = "0x0c" a(6,5) = "0x3e" a(6,6) = "0x63" a(6,7) = "0xc3" a(6,8) = "0xc3" a(6,9) = "0x66" a(6,10) ="0x3c" a(7,1) = "0xff" '数字7 a(7,2) = "0xc0" a(7,3) = "0x60" a(7,4) = "0x30" a(7,5) = "0x18" a(7,6) = "0x18" a(7,7) = "0x18" a(7,8) = "0x18" a(7,9) = "0x18" a(7,10)= "0x18" a(8,1) = "0x3c" '数字8 a(8,2) = "0x66" a(8,3) = "0xc3" a(8,4) = "0x66" a(8,5) = "0x3c" a(8,6) = "0x66" a(8,7) = "0xc3" a(8,8) = "0xc3" a(8,9) = "0x66" a(8,10)= "0x3c" a(9,1) = "0x3c" '数字9 a(9,2) = "0x66" a(9,3) = "0xc3" a(9,4) = "0xc3" a(9,5) = "0x66" a(9,6) = "0x3c" a(9,7) = "0x18" a(9,8) = "0x0c" a(9,9) = "0x06" a(9,10)= "0x03" %> 显示的方法是: 1.先传出一个MIME: Response.ContentType = "image/x-xbitmap" 2.再传出一个c++的源程序,如显示2: #define counter_width 8 #define counter_height 10 static unsigned char counter_bits[] = { 0x3c,0x66,0x60,0x60,0x30,0x18,0x0c,0x06,0x06,0x7e }; 这样在浏览器上就显示出来一个8*10像素的2了 要显示两个或以上的数字的时候,须改动宽度的值(必须是图像点阵宽度的整数倍),在count_bits[]数组的值排序如下: 比如显示 12 a(1,1), a(2,1), a(1,2), a(2,2)... a(1,10), a(2,10) 下面是具体计数器的例子: count.asp <!--#include file="num.asp"--> <% Dim Image Dim Width, Height Dim num Dim digtal Dim Length Dim sort Length = 10 '自定计数器长度 Redim sort( Length ) num = 62275 '计数器的值 digital = "" For I = 1 To Length -Len( num ) '补0 digital = digital & "0" Next For I = 1 To Len( num ) digital = digital & Mid( num, I, 1 ) Next For I = 1 To Len( digital ) sort(I) = Mid( digital, I, 1 ) Next Width = 8 * Len( digital ) '图像的宽度 Height = 10 '图像的高度,在本例中为固定值 Response.ContentType="image/x-xbitmap" hc=chr(13) & chr(10) Image = "#define counter_width " & Width & hc Image = Image & "#define counter_height " & Height & hc Image = Image & "static unsigned char counter_bits[]={" & hc For I = 1 To Height For J = 1 To Length Image = Image & a(sort(J),I) & "," Next Next Image = Left( Image, Len( Image ) - 1 ) '去掉最后一个逗号 Image = Image & "};" & hc Response.Write Image %> 把 #define counter_width 8 #define counter_height 10 static unsigned char counter_bits[] = { 0x3c,0x66,0x60,0x60,0x30,0x18,0x0c,0x06,0x06,0x7e }; 保存为 xbm 文件就可以得到一张显示为 2 的图片 #define counter_width 80 #define counter_height 10 static unsigned char counter_bits[]={ 0x3c,0x3c,0x3c,0x3c,0x3c,0x60,0x3c,0x3c,0xff,0xfe,0x66,0x66,0x66,0x66,0x66,0x30,0x66,0x66,0xc0,0xfe,0xc3,0xc3,0xc3,0xc3,0xc3,0x18,0x60,0x60,0x60,0x06,0xc3,0xc3,0xc3,0xc3,0xc3,0x0c,0x60,0x60,0x30,0x06,0xc3,0xc3,0xc3,0xc3,0xc3,0x3e,0x30,0x30,0x18,0x3e,0xc3,0xc3,0xc3,0xc3,0xc3,0x63,0x18,0x18,0x18,0x60,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0x0c,0x0c,0x18,0xc0,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0x06,0x06,0x18,0xc3,0x66,0x66,0x66,0x66,0x66,0x66,0x06,0x06,0x18,0x66,0x7e,0x7e,0x7e,0x7e,0x7e,0x3c,0x7e,0x7e,0x18,0x3c}; 保存为 xbm文件则显示为 0000062275 。 |


