Discuz 作為國內最流行的論壇系統,雖內建通行證機制,但實際項目中常需與自有用戶系統整合。本文介紹一種無需調用通行證介面、直接操作 Discuz 資料表實現同步登入的方法,核心在於正確生成 Discuz 識別的 Cookie。
Discuz 登入狀態由 auth Cookie 決定,其值經 authcode() 函數加密,包含密碼雜湊、安全提問答案、用戶 UID 三項資訊。只要在我們的系統驗證用戶帳密正確後,按 Discuz 規則寫入相同格式的 Cookie,即可實現同步登入。
<?php
session_start();
define('NOROBOT', TRUE);
define('CURSCRIPT', 'logging');
require_once './include/common.inc.php';
require_once DISCUZ_ROOT.'./include/misc.func.php';
if($action == 'login') {
$field = $loginfield == 'uid' ? 'uid' : 'username';
// 安全碼檢查邏輯(略)
$seccodecheck = substr(sprintf('%05b', $seccodestatus), -2, 1);
if($seccodecheck && $seccodedata['loginfailedcount']) {
$seccodecheck = $db->result($db->query("SELECT count(*) FROM {$tablepre}failedlogins WHERE ip='$onlineip' AND count>='$seccodedata[loginfailedcount]' AND $timestamp-lastupdate<=900"), 0);
}
$discuz_uid = 0;
$discuz_user = $discuz_pw = $discuz_secques = $md5_password = '';
$member = array();
$loginperm = logincheck();
if(!$loginperm) {
showmessage('login_strike');
}
$secques = quescrypt($questionid, $answer);
if(isset($loginauth)) {
$field = 'username';
$password = 'VERIFIED';
list($username, $md5_password) = daddslashes(explode("\t", authcode($loginauth, 'DECODE')), 1);
} else {
$md5_password = md5($password);
$password = preg_replace("/^(.{".round(strlen($password) / 4)."})(.+?)(.{".round(strlen($password) / 6)."})$/s", "\\1***\\3", $password);
}
// 查詢用戶資料
$query = $db->query("SELECT m.uid AS discuz_uid, m.username AS discuz_user, m.password AS discuz_pw, m.secques AS discuz_secques,
m.adminid, m.groupid, m.styleid AS styleidmem, m.lastvisit, m.lastpost, u.allowinvisible
FROM {$tablepre}members m LEFT JOIN {$tablepre}usergroups u USING (groupid)
WHERE m.$field='$username'");
$member = $db->fetch_array($query);
// 驗證密碼
if($member['password'] != $md5_password) {
echo "您輸入的用戶名或密碼錯誤!";
exit();
}
// 登入成功,寫入 Cookie
extract($member);
$discuz_userss = $discuz_user;
$discuz_user = addslashes($discuz_user);
if(($allowinvisible && $loginmode == 'invisible') || $loginmode == 'normal') {
$db->query("UPDATE {$tablepre}members SET invisible='".($loginmode == 'invisible' ? 1 : 0)."' WHERE uid='$member[discuz_uid]'", 'UNBUFFERED');
}
$styleid = intval(empty($_POST['styleid']) ? ($styleidmem ? $styleidmem : $_DCACHE['settings']['styleid']) : $_POST['styleid']);
$cookietime = intval(isset($_POST['cookietime']) ? $_POST['cookietime'] : ($_DCOOKIE['cookietime'] ? $_DCOOKIE['cookietime'] : 0));
dsetcookie('cookietime', $cookietime, 31536000);
dsetcookie('auth', authcode("$discuz_pw\t$discuz_secques\t$discuz_uid", 'ENCODE'), $cookietime);
$sessionexists = 0;
updatesession(); // 更新論壇 USERPASS 數據
echo "{'action':'true'}"; // 自定義返回數據
print_r($_DSESSION);
echo $discuz_userss;
}
?>
common.inc.php:初始化資料庫連線、全域變數、常用函數。misc.func.php:包含 authcode()、dsetcookie()、quescrypt() 等關鍵函數。Discuz 密碼存儲為 md5($password)。代碼中先將用戶提交的明文密碼進行 MD5 雜湊,再與資料庫 password 欄位比對。若一致,提取 uid、password(已加密)、secques 三項關鍵資訊。
dsetcookie('auth', authcode("$discuz_pw\t$discuz_secques\t$discuz_uid", 'ENCODE'), $cookietime);
$discuz_pw:資料庫中已 MD5 加密的密碼。$discuz_secques:安全提問答案的加密值(未設定也需帶入)。$discuz_uid:用戶唯一識別 ID。\t 分隔,經 authcode() 加密後寫入 Cookie。authcode() 函數內部使用常量 UC_KEY(或配置檔中的 $discuz_auth_key)作為加密金鑰。務必確保:
config.inc.php 中的金鑰。misc.func.php 中 authcode() 使用的金鑰。authcode() 函數(若未直接引入)使用的金鑰。三者完全一致,否則 Cookie 無法被論壇識別。
require_once 論壇的 common.inc.php,即可直接使用 $discuz_uid、$discuz_user 等全域變數,最簡潔可靠。authcode()、dsetcookie()、clearcookies() 三個函數至您的 func.php,並確保金鑰一致。| 現象 | 可能原因 | 解決方案 |
|---|---|---|
| 登入後論壇仍顯示未登入 | Cookie 域/路徑不匹配、加密金鑰不一致 | 檢查 dsetcookie() 參數、統一 authcode 金鑰 |
| 提示「您輸入的用戶名或密碼錯誤」 | 密碼加密方式不一致(如雙重 MD5)、字元集導致雜湊值不同 | 確認雙方均為單次 md5($password),檢查資料庫連線字元集 |
| 安全碼/防灌水限制觸發 | 同 IP 短時間多次失敗 | 調整 $seccodedata['loginfailedcount'] 設定或清空 failedlogins 表 |
掌握上述核心機制,您即可在任何 PHP 系統中實現與 Discuz 的無縫同步登入,避免維護兩套用戶表帶來的管理負擔與資源浪費。