使用php生成驗證碼

很久沒發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顏色互換函數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章