唐乾林,黎現(xiàn)云
(重慶電子工程職業(yè)學(xué)院,重慶 401331)
主流驗(yàn)證碼通過提供靜態(tài)的圖片,比較容易被OCR軟件識別;一些網(wǎng)站提供GIF動態(tài)的驗(yàn)證碼圖片,使識別器不容易辨識哪一個圖層是真正的驗(yàn)證碼圖片。這種方法不僅可以提供清晰的圖片,還可以更有效地防止識別器的識別。目前,比較常見的GIF動態(tài)驗(yàn)證碼都是由英文字母或數(shù)字組成的,這實(shí)現(xiàn)起來相對簡單但安全性也相對較弱。為了提高驗(yàn)證碼的安全性,就有了中文GIF動態(tài)驗(yàn)證碼的提法[1]。為了網(wǎng)站和驗(yàn)證碼的安全,為了中文GIF動態(tài)驗(yàn)證碼能得到廣泛的應(yīng)用,本文以代碼復(fù)用和易于維護(hù)為出發(fā)點(diǎn),進(jìn)行了中文GIF動態(tài)驗(yàn)證碼生成器的探索與研究。
要產(chǎn)生中文GIF動態(tài)驗(yàn)證碼就必須能做到隨機(jī)生成漢字,要想能生成漢字就必須熟悉漢字的編碼及其產(chǎn)生方法。
生成隨機(jī)數(shù)的方法很多,不僅可以自己編程實(shí)現(xiàn),也可以復(fù)用現(xiàn)成的代碼。為了方便,本研究使用PHP中的mt_rand或rand函數(shù)完成隨機(jī)數(shù)的生成。rand函數(shù)默認(rèn)使用libc隨機(jī)數(shù)發(fā)生器,很多老的libc的隨機(jī)數(shù)發(fā)生器具有一些不確定和未知的特性而且效率很低;mt_rand則是用了Mersenne Twister中已知的特性作為隨機(jī)數(shù)發(fā)生器,其產(chǎn)生隨機(jī)數(shù)值的平均速度比libc提供的rand快4倍。所以在PHP中mt_rand函數(shù)是非正式用來替換rand的[2]。
語法:
rand(min,max)
mt_rand(min,max)
min,max皆為可選,規(guī)定隨機(jī)數(shù)產(chǎn)生的范圍。
如果沒有提供可選參數(shù)min和max,則返回0~RAND_MAX之間的偽隨機(jī)整數(shù)。例如,想要10~1 000(包括10和1000)之間的隨機(jī)數(shù),用rand(10,1 000)或mt_rand(10,1 000)。
1.2.1 GB2312
GB2312標(biāo)準(zhǔn)共收錄6 763個漢字,其中一級漢字3 755個,其編碼范圍為0xB0A1~0xD7F9;二級漢字3 008個,其編碼范圍為0xD8A1~0xF7FE;同時,GB2312收錄了包括拉丁字母、希臘字母、日文平假名及片假名字母、俄語西里爾字母在內(nèi)的682個全角字符[3]。
GB2312的出現(xiàn),基本滿足了漢字的計(jì)算機(jī)處理需要,它所收錄的漢字已經(jīng)覆蓋中國大陸99.75%的使用頻率。但對于人名、古漢語等方面出現(xiàn)的罕用字GB2312不能處理,這導(dǎo)致了后來GBK及GB18030漢字字符集的出現(xiàn)。
1.2.2 GBK
GBK編碼是在GB2312標(biāo)準(zhǔn)基礎(chǔ)上的內(nèi)碼擴(kuò)展規(guī)范,共收錄了21 003個漢字,完全兼容GB2312標(biāo)準(zhǔn),支持國際標(biāo)準(zhǔn)ISO/IEC10646—1和國家標(biāo)準(zhǔn)GB13000—1中的全部中日韓漢字,并包含了BIG5編碼中的所有漢字。
GBK是采用單雙字節(jié)變長編碼,英文使用單字節(jié)編碼,完全兼容ASCII字符編碼,中文部分采用雙字節(jié)編碼,其編碼范圍為8140~FEFE,首字節(jié)在81~FE,尾字節(jié)在40~FE,剔除 xx7F一條線??傆?jì)23 940個碼位,共收入21 886個漢字和圖形符號,其中漢字(包括部首和構(gòu)件)21 003個,圖形符號883個。
1.2.3 GB18030
GB18030全稱為信息技術(shù)中文編碼字符集,是中華人民共和國國家標(biāo)準(zhǔn)所規(guī)定的變長多字節(jié)字符集。其對GB2312完全向后兼容,與GBK基本向后兼容,并支持Unicode(GB 13000)的所有碼位。GB18030共收錄漢字70 244個。
GB18030采用單字節(jié)、雙字節(jié)和四字節(jié)3種方式對字符編碼。(1)單字節(jié)部分采用GB/T 11383的編碼結(jié)構(gòu)與規(guī)則,使用0x00~0x7F碼位(對應(yīng)于ASCII碼的相應(yīng)碼位)。(2)雙字節(jié)部分,首字節(jié)碼位從0x81至0xFE,尾字節(jié)碼位分別是0x40~0x7E和0x80~0xFE。(3)四字節(jié)部分采用GB/T 11383未采用的0x30~0x39作為對雙字節(jié)編碼擴(kuò)充的后綴,這樣擴(kuò)充的四字節(jié)編碼,其范圍為0x81308130~0xFE39FE39。其中,第一、第三個字節(jié)編碼碼位均為0x81~0xFE,第二、第四個字節(jié)編碼碼位均為0x30~0x39。
1.2.4 Unicode
Unicode、統(tǒng)一碼也叫萬國碼、單一碼是計(jì)算機(jī)科學(xué)領(lǐng)域里的一項(xiàng)業(yè)界標(biāo)準(zhǔn),包括字符集、編碼方案等。Unicode是為了解決傳統(tǒng)的字符編碼方案的局限而產(chǎn)生的,它為每種語言中的每個字符設(shè)定了統(tǒng)一并且唯一的二進(jìn)制編碼,以滿足跨語言、跨平臺進(jìn)行文本轉(zhuǎn)換、處理的要求。1990年開始研發(fā),1994年正式發(fā)布1.0版本,2005年3月31日發(fā)布4.1.0版本,2021年9月14日發(fā)布14.0版本。常用漢字編碼范圍為4E00~9FFF,計(jì)有20 992個。擴(kuò)展?jié)h字編碼范圍為20000~2FA1F,計(jì)有53 424個。所以,Unicode所能表示的漢字有74 416個。
1.2.5 UTF-8
UTF-8是針對Unicode的一種可變長度字符編碼。它可以用來表示Unicode標(biāo)準(zhǔn)中的任何字符,且其編碼中的第一個字節(jié)仍與ASCII相容,使得原來處理ASCII字符的軟件無須或只進(jìn)行少部分修改后,便可繼續(xù)使用。因此,它逐漸成為電子郵件、網(wǎng)頁及其他存儲或傳送文字的應(yīng)用中,優(yōu)先采用的編碼。
Unicode只是一組字符設(shè)定或者說是從數(shù)字和字符之間的邏輯映射的概念編碼,但它并沒有指定代碼點(diǎn)如何在計(jì)算機(jī)上存儲。在Unicode官方資料中,Unicode的編碼方式有三種:UTF-8,UTF-16,UTF-32。由于UTF-8與字節(jié)序無關(guān)即無需BOM,同時兼容ASCII編碼,使得UTF-8編碼成為現(xiàn)今互聯(lián)網(wǎng)信息編碼標(biāo)準(zhǔn)而被廣泛使用。
根據(jù)漢字編碼規(guī)則及前面的分析,隨機(jī)生成的漢字,其編碼可為GB2312的一級漢字或二級漢字,也可以直接采用Unicode編碼。
生成GB2312漢字的PHP代碼如下:
function getChar($num){ // $num為生成漢字的數(shù)量
$b = '';
for ($i=0;$i<$num;$i++){
$b.=iconv('GB2312','UTF-8',chr(mt_rand(0xB0,0xD7)).chr(mt_rand(0xA1,0xF9)));
}
return $b;
}
根據(jù)前面的分析與設(shè)計(jì)及PHP語言,實(shí)現(xiàn)的中文GIF動態(tài)驗(yàn)證碼生成器的主要代碼如下:
//GIF動畫類
class GIFEncoder {
var $GIF = "GIF89a";
var $VER = "GIFEncoder V2.06";
var $BUF = Array ();
var $LOP = 0;
var $DIS = 2;
var $COL = -1;
var $IMG = -1;
var $ERR = Array (
'ERR00' =>"Does not supported function for only one image!",
'ERR01' =>"Source is not a GIF image!",
'ERR02' =>"Unintelligible flag ",
'ERR03' =>"Could not make animation from animated GIF source",
);
…
}
//生成GIF圖片驗(yàn)證
function ImageCode($string='',$width=75,$height=25){
if(empty($string)){
$len = mt_rand(4,6);
$width=16*$len;
$string=getChar($len);
}
$authstr = $string;
$board_width = $width;
$board_height = $height;
// 生成一個32幀的GIF動畫
for($i = 0;$i < 32;$i++){
ob_start();
$image = imagecreate($board_width,$board_height);
imagecolorallocate($image,0,0,0);
// 設(shè)定文字顏色數(shù)組
$colorList[] = imagecolorallocate($image,15,73,210);
$colorList[] = imagecolorallocate($image,0,64,0);
$colorList[] = imagecolorallocate($image,0,0,64);
…
$fontcolor = imagecolorallocate($image,0,0,0);
$gray = imagecolorallocate($image,245,245,245);
$color = imagecolorallocate($image,255,255,255);
$color2 = imagecolorallocate($image,255,0,0);
imagefill($image,0,0,$gray);
$space = 15;// 字符間距
$font=array('C:WindowsFontsmsyhbd.ttc',/*微軟雅黑*/
'C:WindowsFontsSTXINGKA.TTF',//華文行楷
'C:WindowsFontssimsun.ttc'/*宋體&新宋體*/);
if($i > 0){ // 屏蔽第一幀
$top=0;//缺失此項(xiàng),致命錯誤
for ($k = 0;$k < strlen($authstr);$k++){
$colorRandom = mt_rand(0,sizeof($colorList)-1);
$float_top = rand(0,4);
$float = rand(0,3);
$size=mt_rand(13,15);//字體大小
$angle=mt_rand(0,30);//角度制表示的角度
imagettftext($image,$size,$angle,$space*$k,$top+$float_top+15,$colorList[$colorRandom],$font[mt_rand(0,count($font)-1)],mb_substr($authstr,$k,1));//支持中文
}
}
for ($k = 0;$k < 20;$k++){
$colorRandom = mt_rand(0,sizeof($colorList)-1);
imagesetpixel($image,rand()%70,rand()%15,$colorList[$colorRandom]);
}
// 添加干擾線
…
$gif = new GIFEncoder($imagedata);
header('Content-type:image/gif');
echo $gif->GetAnimation();
}
前面使用PHP語言實(shí)現(xiàn)了中文驗(yàn)證碼生成器,那么這個生成器能生成中文驗(yàn)證碼,可以編寫如下的簡單代碼進(jìn)行測試。
//調(diào)用示例
if(isset($_GET["getcode"])&& $_GET["getcode"]==1){
session_start();
$len = mt_rand(4,6);//生成驗(yàn)證碼位數(shù):4-6位的變長驗(yàn)證碼
$w=16*$len;//計(jì)算顯示驗(yàn)證碼的寬度
$_SESSION['timestamp'.session_id()] = time();//設(shè)置有效時間
$randCode=getChar($len);//獲得驗(yàn)證碼
$_SESSION['code'.session_id()]=$randCode;//記錄驗(yàn)證碼
ImageCode($randCode,$w);//顯示GIF動畫
exit();
}
//驗(yàn)證示例
if(isset($_POST['gettxt']))
{
session_start();
exit($_SESSION['code'.session_id()]);
}
//HTML頁面調(diào)用示例
?>
function gettxt(){
$.ajax({
type:"POST",
url:"=$_SERVER['PHP_SELF']?>",
dataType:"html",
data:{
gettxt:1
},
success:function(data){
$("#code").html(data);
},
complete:function(XMLHttpRequest,textStatus){
},
error:function(){
//請求出錯處理
}
});
}
中文GIF動態(tài)驗(yàn)證碼
經(jīng)過反復(fù)運(yùn)行上述代碼,都可以在終端看到隨機(jī)生成的有漢字動態(tài)顯示的GIF動畫,也可以獲得當(dāng)前的中文驗(yàn)證碼(見圖1),說明上述生成器沒有問題,可以投入實(shí)際項(xiàng)目使用。
實(shí)踐證明,本文的中文GIF動態(tài)驗(yàn)證碼生成器是可行的、可用的??梢愿鶕?jù)需要,選用一級漢字或者二級漢字,在簡單的輸入驗(yàn)證中,也是比較安全高效的。若選用Unicode編碼,則有可能出現(xiàn)生僻字,從而使輸入的難度增加,但可以改造成點(diǎn)擊驗(yàn)證,從而可以進(jìn)一步完善此中文驗(yàn)證碼生成器。