一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

DEDECMS|帝國CMS|Discuz|PHPCMS|Wordpress|ZBLOG|ECSHOP|蘋果CMS|極致CMS|CMS系統(tǒng)|

服務(wù)器之家 - 建站程序 - Discuz - Discuz! X2驗證碼的產(chǎn)生和驗證及隨機數(shù)產(chǎn)生探討

Discuz! X2驗證碼的產(chǎn)生和驗證及隨機數(shù)產(chǎn)生探討

2019-09-25 14:41Discuz教程網(wǎng) Discuz

本文將詳細(xì)介紹下Discuz! X2驗證碼的產(chǎn)生和驗證碼的驗證以及通過PHP方式的驗證碼驗證實例講解另外還有隨機數(shù)如何產(chǎn)生的介紹,感興趣的你可以參考下,希望可以幫助到你

一、驗證碼的產(chǎn)生 
1、如何在模板中添加一個驗證碼 
在X2中驗證碼的模板部分獨立為一個模板文件(template/default/common/seccheck.htm),供各個地方調(diào)用。 
在模板中可以添加如下代碼來調(diào)用驗證碼模板部分: 

復(fù)制代碼

代碼如下:


<!--{eval $seccodecheck = 1;}--> 
<!--{eval $sectpl = '<tr><th><sec></th><td><sec><p class="d"><sec></p></td>';}--> 
<!--{subtemplate common/seccheck}--> 


解釋下這三句話: 

第一句的意思為,我要開啟驗證碼,即 $seccodecheck 
變量 
必須為真,就表示當(dāng)前頁面要開啟驗證碼。 

第二句的意思為,給要顯示出來的驗證碼設(shè)置一個顯示的模板格式,$sectpl 這個變量對應(yīng)的就是模板,設(shè)置 $sectpl 可以讓驗證碼的顯示與當(dāng)前頁面的格式更好的結(jié)合。從示例的模板代碼中可以看出,只有 <sec> 不屬于 HMTL 標(biāo)準(zhǔn)代碼,而且出現(xiàn)了 3 次,這 3 次分別代表:“驗證碼”文字、驗證碼輸入框、驗證碼圖片,如下圖所示: 
Discuz! X2驗證碼的產(chǎn)生和驗證及隨機數(shù)產(chǎn)生探討 
這樣就可以把驗證碼不同的部分合理的安放在您的頁面中了。 

第三句的意思為,將獨立的驗證碼模板合并到當(dāng)前頁面中,與當(dāng)前頁面的模板一同輸出。 

在模板中添加上如上的代碼后,刷新頁面就可以看到驗證碼部分了。 

2、驗證碼的生成流程 
(以X2默認(rèn)設(shè)置的“英文圖片驗證碼”為例) 

1)剛出現(xiàn)的驗證碼會默認(rèn)執(zhí)行一段 JS 代碼 

復(fù)制代碼

代碼如下:


<script type="text/javascript" reload="1">updateseccode('SQq29j20');</script> 


執(zhí) 行的 JS 主要就是執(zhí)行了 updateseccode 這個函數(shù),直接點擊驗證碼圖片執(zhí)行的也是這個函數(shù)。函數(shù)中的 'SQq29j20' 是當(dāng)前頁面驗證碼的唯一字符串 idhash,他是由是否為Ajax請求、session id、自增數(shù)字組成,此處不必深究其含義。 

2)updateseccode 函數(shù)在 static/js/common.js 中 

復(fù)制代碼

代碼如下:


function updateseccode(idhash, play) { 
$F('_updateseccode', arguments); 


通過上面代碼可以看到,updateseccode 又調(diào)用了 _updateseccode 私有函數(shù),_updateseccode 函數(shù)在 static/js/common_extra.js 文件中 

復(fù)制代碼

代碼如下:


function _updateseccode(idhash, play) { 
if(isUndefined(play)) { 
if($('seccode_' + idhash)) { 
$('seccodeverify_' + idhash).value = ''; 
if(secST['code_' + idhash]) { 
clearTimeout(secST['code_' + idhash]); 

$('checkseccodeverify_' + idhash).innerHTML = '<img src="'+ IMGDIR + '/none.gif" width="16" height="16" class="vm" />'; 
ajaxget('misc.php?mod=seccode&action=update&idhash=' + idhash, 'seccode_' + idhash, null, '', '', function() { 
secST['code_' + idhash] = setTimeout(function() {$('seccode_' + idhash).innerHTML = '<span class="xi2 cur1" onclick="updateseccode(''+idhash+'')">刷新驗證碼</span>';}, 180000); 
}); 

} else { 
eval('window.document.seccodeplayer_' + idhash + '.SetVariable("isPlay", "1")'); 


這段 JS 代碼有兩個含義: 

一是通過 ajaxget 請求了 misc.php?mod=seccode&action=update&idhash=xxxx 這樣一個地址 

二是設(shè)定了一個 
定時器 
,從顯示了驗證碼開始,3分鐘后自動將驗證碼圖片換為“刷新驗證碼”的文字,點擊該文字就執(zhí)行 updateseccode 這個函數(shù),重新更新驗證碼。由此可以看出,此種方式可以很好的解決驗證碼過期的問題。 

3)找到通過 ajaxget 請求的程序 source/module/misc/misc_seccode.php 

通過 url 中的 action=update 可以看出,應(yīng)該查看 if($_G['gp_action'] == 'update') { …… } 中的一段 

復(fù)制代碼

代碼如下:


if($_G['gp_action'] == 'update') { 
$message = ''; 
if($_G['setting']['seccodestatus']) { 
$rand = random(5, 1); 
$flashcode = ''; 
$idhash = isset($_G['gp_idhash']) ? $_G['gp_idhash'] : ''; 
$ani = $_G['setting']['seccodedata']['animator'] ? '_ani' : ''; 
if($_G['setting']['seccodedata']['type'] == 2) { 
…… 
} elseif($_G['setting']['seccodedata']['type'] == 3) { 
…... 
} else { 
$message = lang('core', 'seccode_image'.$ani.'_tips').'<img onclick="updateseccode(''.$idhash.'')" width="'.$_G['setting']['seccodedata']['width'].'" height="'.$_G['setting']['seccodedata']['height'].'" src="misc.php?mod=seccode&update='.$rand.'&idhash='.$idhash.'" class="vm" /> 默 認(rèn)設(shè)置的“英文圖片驗證碼”的 $_G['setting']['seccodedata']['type'] 為 0,所以看 else 的部分。仔細(xì)看這里就是按照 ajax 的格式返回了一個驗證碼的圖片,但是圖片的 src 為 misc.php?mod=seccode&update=$rand&idhash=$idhash 這樣一個動態(tài)鏈接,所以是通過這個鏈接動態(tài)生成的圖片,此時又產(chǎn)生了一個新的請求。 

4)找到通過圖片鏈接請求的程序 source/module/misc/misc_seccode.php(和上面是同一個文件) 

通過 url 可以看出,應(yīng)該查看 if($_G['gp_action'] == 'update') { …… } else { …… } 中的一段 

復(fù)制代碼

代碼如下:


} else { 
$refererhost = parse_url($_SERVER['HTTP_REFERER']); 
$refererhost['host'] .= !empty($refererhost['port']) ? (':'.$refererhost['port']) : ''; 
if($_G['setting']['seccodedata']['type'] < 2 && ($refererhost['host'] != $_SERVER['HTTP_HOST'] || !$_G['setting']['seccodestatus']) || $_G['setting']['seccodedata']['type'] == 2 && !extension_loaded('ming') && $_POST['fromFlash'] != 1 || $_G['setting']['seccodedata']['type'] == 3 && $_GET['fromFlash'] != 1) { 
exit('Access Denied'); 

$seccode = make_seccode($_G['gp_idhash']); 
if(!$_G['setting']['nocacheheaders']) { 
@header("Expires: -1"); 
@header("Cache-Control: no-store, private, post-check=0, pre-check=0, max-age=0", FALSE); 
@header("Pragma: no-cache"); 

require_once libfile('class/seccode'); 
$code = new seccode(); 
$code->code = $seccode; 
$code->type = $_G['setting']['seccodedata']['type']; 
$code->width = $_G['setting']['seccodedata']['width']; 
$code->height = $_G['setting']['seccodedata']['height']; 
$code->background = $_G['setting']['seccodedata']['background']; 
$code->adulterate = $_G['setting']['seccodedata']['adulterate']; 
$code->ttf = $_G['setting']['seccodedata']['ttf']; 
$code->angle = $_G['setting']['seccodedata']['angle']; 
$code->warping = $_G['setting']['seccodedata']['warping']; 
$code->scatter = $_G['setting']['seccodedata']['scatter']; 
$code->color = $_G['setting']['seccodedata']['color']; 
$code->size = $_G['setting']['seccodedata']['size']; 
$code->shadow = $_G['setting']['seccodedata']['shadow']; 
$code->animator = $_G['setting']['seccodedata']['animator']; 
$code->fontpath = DISCUZ_ROOT.'./static/image/seccode/font/'; 
$code->datapath = DISCUZ_ROOT.'./static/image/seccode/'; 
$code->includepath = DISCUZ_ROOT.'./source/class/'; 
$code->display(); 


這部分開始是先做了一些安全性的驗證,最后是根據(jù)給定的參數(shù)和由 make_seccode 生成的驗證碼字符串,生成驗證碼的圖片,所以中間是重點。 

make_seccode($_G['gp_idhash']) 這個函數(shù)傳入了當(dāng)前頁面驗證碼的唯一字符串 idhash,生成了用于驗證碼的字符串。 

5)make_seccode 函數(shù)在 source/function/function_seccode.php 文件 

復(fù)制代碼

代碼如下:


function make_seccode($idhash){ 
global $_G; 
$seccode = random(6, 1); 
$seccodeunits = ''; 
if($_G['setting']['seccodedata']['type'] == 1) { 
$lang = lang('seccode'); 
$len = strtoupper(CHARSET) == 'GBK' ? 2 : 3; 
$code = array(substr($seccode, 0, 3), substr($seccode, 3, 3)); 
$seccode = ''; 
for($i = 0; $i < 2; $i++) { 
$seccode .= substr($lang['chn'], $code[$i] * $len, $len); 

} elseif($_G['setting']['seccodedata']['type'] == 3) { 
$s = sprintf('%04s', base_convert($seccode, 10, 20)); 
$seccodeunits = 'CEFHKLMNOPQRSTUVWXYZ'; 
} else { 
$s = sprintf('%04s', base_convert($seccode, 10, 24)); 
$seccodeunits = 'BCEFGHJKMPQRTVWXY2346789'; 

if($seccodeunits) { 
$seccode = ''; 
for($i = 0; $i < 4; $i++) { 
$unit = ord($s{$i}); 
$seccode .= ($unit >= 0x30 && $unit <= 0x39) ? $seccodeunits[$unit - 0x30] : $seccodeunits[$unit - 0x57]; 


dsetcookie('seccode'.$idhash, authcode(strtoupper($seccode)."t".(TIMESTAMP - 180)."t".$idhash."t".FORMHASH, 'ENCODE', $_G['config']['security']['authkey']), 0, 1, true); 
return $seccode; 


從函數(shù)中可以看到,驗證碼 $seccode 首先來自一個6位的隨機數(shù)字 random(6, 1) (此函數(shù)如何工作,最后講解)。 

默認(rèn)設(shè)置的“英文圖片驗證碼”的 $_G['setting']['seccodedata']['type'] 為 0,所以看 else 的部分。將 $seccode 的數(shù)字通過 base_convert 函數(shù)由 10 
進(jìn)制 
轉(zhuǎn)為 24 進(jìn)制,然后設(shè)定可以在驗證碼出現(xiàn)的字符串 

復(fù)制代碼

代碼如下:


'BCEFGHJKMPQRTVWXY2346789'。 


最后將 24 進(jìn)制的驗證碼在 $seccodeunits 中取得真正的 4 位驗證碼字符串 $seccode ,最后將 $seccode 通過 authcode 加密函數(shù)進(jìn)行加密,寫入 cookie 中,并返回,cookie 的名字是 seccode 連上 $idhash 的值(例如:seccodeSQq29j20)。加密時使用的是在 config/config_global.php 中設(shè)置的 $_G['config']['security']['authkey'] 的值。 

至此驗證碼及圖片生成完畢,生成的驗證碼到目前為止只以加密的方式存在于 cookie 中。 

二、驗證碼的驗證 
1、JS 方式的驗證 
1)這種驗證就是在文本框中輸入驗證碼后,及時的驗證。 
Discuz! X2驗證碼的產(chǎn)生和驗證及隨機數(shù)產(chǎn)生探討 
這個驗證是由文本框的 onblur 失去焦點事件觸發(fā) checksec('code', 'SQq29j20') JS 函數(shù)進(jìn)行驗證的。 

2)checksec 函數(shù)在 static/js/common.js 中 

復(fù)制代碼

代碼如下:


function checksec(type, idhash, showmsg, recall) { 
$F('_checksec', arguments); 


通過上面代碼可以看到,checksec 又調(diào)用了 _checksec 私有函數(shù),_checksec 函數(shù)在 static/js/common_extra.js 文件中 

復(fù)制代碼

代碼如下:


function _checksec(type, idhash, showmsg, recall) { 
var showmsg = !showmsg ? 0 : showmsg; 
var secverify = $('sec' + type + 'verify_' + idhash).value; 
if(!secverify) { 
return; 

var x = new Ajax('XML', 'checksec' + type + 'verify_' + idhash); 
x.loading = ''; 
$('checksec' + type + 'verify_' + idhash).innerHTML = '<img src="'+ IMGDIR + '/loading.gif" width="16" height="16" class="vm" />'; 
x.get('misc.php?mod=sec' + type + '&action=check&inajax=1&&idhash=' + idhash + '&secverify=' + (BROWSER.ie && document.charset == 'utf-8' ? encodeURIComponent(secverify) : secverify), function(s){ 
var obj = $('checksec' + type + 'verify_' + idhash); 
obj.style.display = ''; 
if(s.substr(0, 7) == 'succeed') { 
obj.innerHTML = '<img src="'+ IMGDIR + '/check_right.gif" width="16" height="16" class="vm" />'; 
if(showmsg) { 
recall(1); 

} else { 
obj.innerHTML = '<img src="'+ IMGDIR + '/check_error.gif" width="16" height="16" class="vm" />'; 
if(showmsg) { 
if(type == 'code') { 
showError('驗證碼錯誤,請重新填寫'); 
} else if(type == 'qaa') { 
showError('驗證問答錯誤,請重新填寫'); 

recall(0); 


}); 


這 個函數(shù)首先驗證下,輸入框內(nèi)填寫的驗證碼的值 $('sec' + type + 'verify_' + idhash).value 是否存在(type 就是傳入的 code)。然后通過 ajax 請求訪問 misc.php?mod=seccode&action=check&inajax=1&&idhash=xxxx&secverify=xxxx 這樣一個地址,這個地址會返回驗證的結(jié)果字符串。如果返回結(jié)果的前 7 個字符是 succeed 則驗證通過,顯示對勾;否則提示“驗證碼錯誤,請重新填寫”,并顯示紅叉。 

3)找到通過 ajax 請求的程序 source/module/misc/misc_seccode.php 

通過 url 中的 action=check 可以看出,應(yīng)該查看 elseif($_G['gp_action'] == 'check') { …… } 中的一段 

復(fù)制代碼

代碼如下:


} elseif($_G['gp_action'] == 'check') { 
include template('common/header_ajax'); 
echo check_seccode($_G['gp_secverify'], $_G['gp_idhash']) ? 'succeed' : 'invalid'; 
include template('common/footer_ajax'); 
} else { 


這 里將通過 url 傳入的 secverify 和 idhash 兩個值傳遞給 check_seccode 函數(shù),通過代碼看到 check_seccode 返回布爾值,故結(jié)果為真,則通過驗證,返回 succeed 字符串,結(jié)果為假,則驗證失敗,返回 invalid 字符串。 

4)check_seccode 函數(shù)在 source/function/function_core.php 文件 

復(fù)制代碼

代碼如下:


function check_seccode($value, $idhash) { 
global $_G; 
if(!$_G['setting']['seccodestatus']) { 
return true; 

if(!isset($_G['cookie']['seccode'.$idhash])) { 
return false; 

list($checkvalue, $checktime, $checkidhash, $checkformhash) = explode("t", authcode($_G['cookie']['seccode'.$idhash], 'DECODE', $_G['config']['security']['authkey'])); 
return $checkvalue == strtoupper($value) && TIMESTAMP - 180 > $checktime && $checkidhash == $idhash && FORMHASH == $checkformhash; 


此函數(shù)首先根據(jù)緩存中的設(shè)定驗證驗證碼的開啟狀態(tài),如果未開啟,此處驗證直接返回真,既然沒有開啟驗證碼自然如何驗證均為真。 

然后驗證 cookie 中是否存在生成驗證碼時寫入 cookie 的值(例如:seccodeSQq29j20),如果 cookie 沒有此值,則此次驗證失效,需要重新生成驗證碼,重新驗證。 

最后從 cookie 取出值,使用 $_G['config']['security']['authkey'] 加密串,通過 authcode 函數(shù)對值進(jìn)行解密,解密后獲取到驗證碼、生成時間、idhash、formhash 四個值。然后需要同時滿足以下四個條件才可以通過驗證: 

- 輸入的驗證碼等于解密出來的驗證碼 

- 驗證碼的生成時間距當(dāng)前時間小于 180 秒 

- 傳入的 idhash 等于解密出來的 idhash 

- 當(dāng)前系統(tǒng)生成的 formhash 等于解密出來的 formhash 

至此通過 JS 方式的驗證碼驗證完成。 

2、PHP 方式的驗證 
1)這種方式就是在驗證碼所在的表單提交后,對輸入的驗證碼進(jìn)行的驗證。 

例如在修改用戶密碼時開啟了驗證碼,則會在其處理的 PHP 程序中發(fā)現(xiàn)(source/include/spacecp/spacecp_profile.php)這樣一句代碼 
submitcheck('passwordsubmit', 0, $seccodecheck, $secqaacheck) 
submitcheck 函數(shù)就是對提交的表單進(jìn)行驗證的。 

2)submitcheck 函數(shù)在 source/function/function_core.php 文件 

復(fù)制代碼

代碼如下:


function submitcheck($var, $allowget = 0, $seccodecheck = 0, $secqaacheck = 0) { 
if(!getgpc($var)) { 
return FALSE; 
} else { 
global $_G; 
if($allowget || ($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_G['gp_formhash']) && $_G['gp_formhash'] == formhash() && empty($_SERVER['HTTP_X_FLASH_VERSION']) && (empty($_SERVER['HTTP_REFERER']) || 
preg_replace("/https?://([^:/]+).*/i", "1", $_SERVER['HTTP_REFERER']) == preg_replace("/([^:]+).*/", "1", $_SERVER['HTTP_HOST'])))) { 
if(checkperm('seccode')) { 
if($secqaacheck && !check_secqaa($_G['gp_secanswer'], $_G['gp_sechash'])) { 
showmessage('submit_secqaa_invalid'); 

if($seccodecheck && !check_seccode($_G['gp_seccodeverify'], $_G['gp_sechash'])) { 
showmessage('submit_seccode_invalid'); 


return TRUE; 
} else { 
showmessage('submit_invalid'); 



submitcheck 函數(shù)一般只填寫前兩個參數(shù)即可,第一個參數(shù)表示要驗證的表單元素的名字,此表單元素不存在則驗證失敗;第二個參數(shù)表示是否允許通過 GET 方式提交的數(shù)據(jù)通過驗證,0 為不允許,1 為允許,一般為 0 即可。 

后兩個參數(shù)用于表示提交的表單中是否需要對驗證碼和驗證問答做驗證,第三個參數(shù) $seccodecheck 代表驗證碼,第四個參數(shù) $secqaacheck 代表驗證問答,參數(shù)值都是 0 為不驗證,1 為驗證。 

所以如果需要在提交后驗證驗證碼,則至少要填寫 3 個參數(shù),即 submitcheck('passwordsubmit', 0, 1) 。 

進(jìn)入函數(shù)中會現(xiàn)對提交表單的提交方式、formhash、訪問來源 referer 等數(shù)據(jù)進(jìn)行安全性驗證,通過后則會調(diào)用 check_seccode 函數(shù)對提交過來的驗證碼進(jìn)行驗證了,根據(jù) check_seccode 的返回值,來給予不同的提示。 check_seccode 函數(shù)如何工作的參看 JS 驗證中的 4) 即可。 

至此通過 PHP 方式的驗證碼驗證完成。 

三、隨機數(shù)如何產(chǎn)生的 
Discuz! X的隨機數(shù)是通過 random 函數(shù)產(chǎn)生的,函數(shù)在 source/function/function_core.php 文件 

復(fù)制代碼

代碼如下:


function random($length, $numeric = 0) { 
$seed = base_convert(md5(microtime().$_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35); 
$seed = $numeric ? (str_replace('0', '', $seed).'012340567890') : ($seed.'zZ'.strtoupper($seed)); 
$hash = ''; 
$max = strlen($seed) - 1; 
for($i = 0; $i < $length; $i++) { 
$hash .= $seed{mt_rand(0, $max)}; 

return $hash; 


此函數(shù)有兩個參數(shù),$length 表示要獲取的隨機數(shù)的位數(shù),$numeric 表示是否要獲取純數(shù)字的隨機數(shù),取值 0 或 1。 

函數(shù)首先使用 microtime 函數(shù)獲取當(dāng)前的微秒級時間戳字符串,然后在后面拼接上單前網(wǎng)站的根目錄路徑,然后進(jìn)行 MD5 加密,獲得 32 位長的字符串。之后對其進(jìn)行轉(zhuǎn)進(jìn)制,如果要獲取純數(shù)字的隨機數(shù),則從 16 進(jìn)制轉(zhuǎn)為 10 進(jìn)制,如果要獲得數(shù)字和英文混雜的隨機數(shù),則從 16 進(jìn)制轉(zhuǎn)為 35 進(jìn)制。之后再將轉(zhuǎn)進(jìn)制后獲得的字符串,根據(jù)是否要獲取純數(shù)字隨機數(shù)的區(qū)別,進(jìn)行拼接。最后從拼接后的字符串中隨機抽取隨機數(shù)的第一位、第二位以此類推,直 至獲取滿足要求的隨機數(shù)的位置為止。至此生成了隨機數(shù)。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产在线精品一区二区高清不卡 | 太大了轻点阿受不了小说h 四色6677最新永久网站 | 麻豆网站在线看 | 午夜精品国产自在现线拍 | 高清视频一区二区三区 | 女女性恋爱视频入口 | 亚洲天堂网站在线 | 忘忧草在线 | 日韩永久在线观看免费视频 | 99视频精品全部 在线 | 人人艹在线视频 | 男gay网站视频免费观看 | 暖暖高清日本在线 | 免费观看成年肉动漫网站 | 精品一二三区久久AAA片 | 天天性综合 | 91噜噜噜噜色 | 国产老太婆hd老头 | 1024免费福利永久观看网站 | 青青青在线视频播放 | 欧美性色黄大片四虎影视 | 波多野结衣作品在线观看 | 高清一级做a爱免费视 | 亚州笫一色惰网站 | 日本欧美一二三区色视频 | 国产v日韩v欧美v精品专区 | 福利视频导航大全 | 金莲你下面好紧夹得我好爽 | 五月天网站 | asspics大尿chinese | 国产91精品久久久久久 | 成年人网站免费在线观看 | 丝瓜视频黄色在线观看 | 70岁多老妇人特黄a级毛片 | 亚洲丰满模特裸做爰 | 微拍秒拍99福利精品小视频 | 色吊丝每日永久访问网站 | 唯美 清纯 另类 亚洲制服 | 黑人巨摘花第一次出血 | 国产成人精品综合在线观看 | bestialityvideo另类 |