目前,不少网站为了防止用户利用机器人自动注册、登录、灌水,都采用了验证码技术。所谓验证码,就是将一串随机产生的数字或符号,生成一幅图片,图片里加上一些干扰象素(防止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 。