使用php生成验证码

来源:互联网 发布:日本动漫 知乎 编辑:程序博客网 时间:2024/06/05 10:16
很久没发blog了,呵呵,今天讨论一下验证码生成的问题。
常上网的人都知道很多网站在发帖时都要输入验证码,这是因为网站为防止别人使用程序在网站上自动发布大量的垃圾信息(如果是人为的危害就不是太大,不可能短时间发个几千甚至上万信息)。因此就诞生了验证码,在发贴时要验证码正确才能发表成功。这方法大概是现在为止最简单有效的方法了,因为要使用程序去辨认上面的字符难度系数相当高。我使用php写了个验证码生成程序,生成的验证码效果如下:
程序比较简单。原理就是先产生4个随机数,将4个数字生成图片,并将图片进行一定角度的旋转,再合成为一副图片,再加上干扰点和线,考虑到便于用户辨认只使用了数字,干扰线也不能使用太多,这里用了5条。有些网站还有大小写的字母,(呵呵,如果你想让使用户为辨认上面的字符而感到头疼就这么干)。
该方法的缺点是:不是每次产生的图片用户都容易辨认,因此在使用时要考虑用户可以跟换图片,就如新浪blog在发表评论那样。实现的代码如下:
function RgbToHsv($R, $G, $B)
{
 // r,g,b values are from 0 to 1
  // h = [0,360], s = [0,1], v = [0,1]
  // if s == 0, then h = -1 (undefined)

 $tmp = min($R, $G);
  $min = min($tmp, $B);
  $tmp = max($R, $G);
  $max = max($tmp, $B);
  $V = $max;
  $delta = $max - $min;

  if($max != 0)
   $S = $delta / $max; // s
  else
  {
   $S = 0;
    //$H = UNDEFINEDCOLOR;
    return;
  }
  if($R == $max)
   $H = ($G - $B) / $delta; // between yellow & magenta
  else if($G == $max)
    $H = 2 + ($B - $R) / $delta; // between cyan & yellow
  else
    $H = 4 + ($R - $G) / $delta; // between magenta & cyan

  $H *= 60; // degrees
  if($H < 0)
   $H += 360;
  return array($H, $S, $V);
}

function HsvToRgb($H, $S, $V)
{
 if($S == 0)
  {
   // achromatic (grey)
   $R = $G = $B = $V;
    return;
  }

  $H /= 60;  // sector 0 to 5
  $i = floor($H);
  $f = $H - $i;  // factorial part of h
  $p = $V * (1 - $S);
  $q = $V * (1 - $S * $f);
  $t = $V * (1 - $S * (1 - $f));

  switch($i)
  {
   case 0:
     $R = $V;
      $G = $t;
      $B = $p;
      break;
    case 1:
      $R = $q;
      $G = $V;
      $B = $p;
      break;
    case 2:
      $R = $p;
      $G = $V;
      $B = $t;
      break;
    case 3:
      $R = $p;
      $G = $q;
      $B = $V;
      break;
    case 4:
      $R = $t;
      $G = $p;
      $B = $V;
      break;
    default: // case 5:
      $R = $V;
      $G = $p;
      $B = $q;
      break;
 }
  return array($R, $G, $B);
}

$size = 20;
$width = 80;
$height = 25;
// 产生4个随机字符

$randStr =array(rand(0, 9), rand(0, 9), rand(0, 9), rand(0, 9));  

// 生成数字旋转角度

$degrees = array(rand(0, 45), rand(0, 45), rand(0, 45), rand(0, 45)); 

for($i = 0; $i < 4; ++$i)
{
 if(rand() % 2);
 else $degrees[$i] = -$degrees[$i];
}

$image = imagecreatetruecolor($size, $size);   // 数字图片画布
$validate = imagecreatetruecolor($width, $height);  // 最终验证码画布
$back = imagecolorallocate($image, 255, 255, 255);  // 背景色
$border = imagecolorallocate($image, 0, 0, 0);    // 边框

// 数字颜色
for($i = 0; $i < 4; ++$i)
{
 // 考虑为使字符容易看清使用颜色较暗的颜色
 $temp = RgbToHsv(rand(0, 255), rand(0, 255), rand(0, 255));
 
 if($temp[2] > 60)
  $temp [2] = 60;

 $temp = HsvToRgb($temp[0], $temp[1], $temp[2]);
 $textcolor[$i] = imagecolorallocate($image, $temp[0], $temp[1], $temp[2]);
}

imagefilledrectangle($validate, 0, 0, $width - 1, $height - 1, $back);  // 画出背景色
imagefilledrectangle($image, 0, 0, $size, $size, $back); // 画出背景色 
imagestring($image, 5, 6, 2, $randStr[0], $textcolor[0]);  // 画出数字
$image = imagerotate($image, $degrees[0], $back);
imagecopy($validate, $image, 1, 4, 4, 5, imagesx($image) - 10, imagesy($image) - 10);

$image = imagecreatetruecolor($size, $size);
imagefilledrectangle($image, 0, 0, $size, $size, $back);  // 画出背景色 
imagestring($image, 5, 6, 2, $randStr[1], $textcolor[1]);  // 画出数字
$image = imagerotate($image, $degrees[1], $back);
imagecopy($validate, $image, 21, 4, 4, 5, imagesx($image) - 10, imagesy($image) - 10);

$image = imagecreatetruecolor($size, $size);
imagefilledrectangle($image, 0, 0, $size - 1, $size - 1, $back);  // 画出背景色 
imagestring($image, 5, 6, 2, $randStr[2], $textcolor[2]);  // 画出数字
$image = imagerotate($image, $degrees[2], $back);
imagecopy($validate, $image, 41, 4, 4, 5, imagesx($image) - 10, imagesy($image) - 10);

$image = imagecreatetruecolor($size, $size);
imagefilledrectangle($image, 0, 0, $size - 1, $size - 1, $back);  // 画出背景色 
imagestring($image, 5, 6, 2, $randStr[3], $textcolor[3]);  // 画出数字
$image = imagerotate($image, $degrees[3], $back);
imagecopy($validate, $image, 61, 4, 4, 5, imagesx($image) - 10, imagesy($image) - 10);
imagerectangle($validate, 0, 0, $width - 1, $height - 1, $border);  // 画出边框

for($i = 0; $i < 200; ++$i) //加入干扰象素
{
 $randpixelcolor = ImageColorallocate($validate, rand(0, 255), rand(0, 255), rand(0, 255));
 imagesetpixel($validate, rand(1, 87), rand(1, 27), $randpixelcolor);
}

// 干扰线使用颜色较明亮的颜色
$temp = RgbToHsv(rand(0, 255), rand(0, 255), rand(0, 255));

if($temp[2] < 200)
 $temp [2] = 255;
 
$temp = HsvToRgb($temp[0], $temp[1], $temp[2]);
$randlinecolor = imagecolorallocate($image, $temp[0], $temp[1], $temp[2]);
// 画5条干扰线
for ($i = 0;$i < 5; $i ++)
 imageline($validate, rand(1, 79), rand(1, 24), rand(1, 79), rand(1, 24), $randpixelcolor);

header('Content-type: image/png');
imagepng($validate);
imagedestroy($validate);

最后讨论一下新浪博客中的“收听验证码”,呵呵,看起来好像很复杂,考虑了一下,其实要实现也挺简单的,将单个数字录成mp3格式的声音文件,使用windows media player根据产生的数字播放出来(网页中嵌入个object)。考虑兼容问题最好使用flash播放。

提示一下:程序中有Rgb到Hsv颜色互换函数。