本欄目下相關鏈接
Discuz! Passport 接口技術文檔
從 Discuz! 4.0.0 RC4 版本開始，Discuz! 內嵌了一個獨特的 Passport（通行證） 接口，利用此接口，用戶將很容易將論壇與其他應用 程序整合，而實現統一登錄與退出、用戶數據共享、積分同步等功能。可以整合的應用程序包括內容管理系統（CMS）、商城系統、遊戲系統等 等，如您對這方面功能有興趣或有需求，請繼續閱讀本文檔。 Discuz! Passport 的優點 Discuz! Passport 系統使用了 Discuz! 獨有的技術，並不等同於以往使用過的一些方法，與傳統的實現方式相比，具備（不限於）以下優勢： 基於私有密匙的低相關性可逆加密算法，配合 MD5 校檢碼技術，使得暴力破解或偽造幾乎不可能。 應用程序可與論壇放置於不同的服務器及不同的域名下。可基於不同操作系統、不同程序語言和不同數據庫平台，具備真正的平台無關性。 不需要任何形式的數據庫連接、或強制把兩套應用程序的數據放在同一數據庫甚至同一數據表中。論壇與應用程序都有各自的用戶數據表， 只是在需要時進行無縫同步操作。 對應用程序的代碼改動簡便易行，可最快速的完成應用程序與論壇間的整合。 Discuz! Passport 的局限 您在開始利用 Discuz! Passport 進行二次開發時，需要瞭解這個系統的局限性，以對未來的工作進行正確的評估與安排。 只能工作在用戶密碼不加密、可逆加密或 MD5 加密的情況下，否則論壇後台無法登錄。 只能與一種應用程序關聯，即二方關聯。不能實現三方關聯或與更多的應用程序進行關聯。 應用程序需具有獨立的註冊、登錄、退出頁面和鏈接，否則需要自行修改論壇中的相應表單或程序。 由於論壇的註冊人數可能很多，例如百萬級以上，且應用程序和論壇間的用戶數據是同步的，因此要求應用程序能夠穩定的負載大量用戶的 訪問。
Discuz! Passport 原理與流程
假設已設置如下變量或參數 掛接 Discuz! Passport 的應用程序假設為一套 PHP 語言編寫的 CMS 系統 Discuz! 的 URL 為 http://www.myforums.com 應用程序的 URL 為 http://www.mywebsite.com 應用程序的註冊頁面為 http://www.mywebsite.com/register.php 應用程序的登錄頁面為 http://www.mywebsite.com/login.php?action=login 應用程序的退出頁面為 http://www.mywebsite.com/login.php?action=logout 開啟通行證後的用戶登錄流程 如果用戶在論壇點擊「登錄」，則轉向到事先設置好的應用程序登錄頁面（http://www.mywebsite.com/login.php?action=login），並在登錄頁面 的 URL 中加入參數 forward（加入 forward 後的鏈接例如 http://www.mywebsite.com/login.php?action=login&forward=http://www.myforums.com/index.php）， 用於在登錄後將用戶導向到指定的 URL。 應用程序收到此請求後，按照慣例生成表單，並增加一個表單變量 ， 將 GET 方式傳遞過來的 forward 參數通過表單進行傳遞。 用戶在應用程序的表單中填寫登錄信息，並提交到應用程序的登錄驗證程序。應用程序驗證用戶提交的用戶名和密碼的合法性： 如果不通過：提示用戶名密碼錯誤，要求其返回上一頁重新填寫。 如果通過，需要進行如下操作： 設置自身 Cookie 或 Session，使得應用程序自身處於登錄狀態。 檢查表單中是否提交了 forward 變量，如有，則意味著登錄請求可能是由論壇而來，將此變量傳遞到後面的請求中。如沒有，自行生成 forward 變量，使得論壇登錄後能夠跳轉回到應用程序中。 通過 header('Location: http://www.myforums.com/api/passport.php?action=login&auth=xxx&forward=http://yyy&verify=zzz') 的 方式，將登錄請求傳遞到論壇進行處理。其中 auth 用來將用戶信息與資料以特定的格式，加密傳遞給論壇，forward 用於告知論壇 Passport API 完成 自身操作後轉向到的 URL 地址，verify 用於驗證前面兩個變量的有效性。auth、forward、verify 格式與結構將在後面進行說明。 Discuz! Passport API 在接收到由應用程序通過 header() 提交過來的請求後，進行如下操作： 根據 verify 判斷 auth 和 forward 變量是否合法，如合法則繼續，否則終止。 將 auth 根據既定算法解密，並還原成數組，數組的內容與格式將在後面進行說明。根據數組中的內容，檢查此用戶是否存在。如存在，則根據 上述數組中的內容 UPDATE 論壇中相應的用戶資料。如不存在，則使用數組中的信息 INSERT 到論壇用戶資料表中。 論壇設置 Cookie 或 Session，使得論壇自身處於登錄狀態。 根據應用程序反饋的 forward 值，通過 header('Location: http://xxx') 的形式將頁面跳轉到 forward 變量指定的 URL。 至此，登錄流程結束 開啟通行證後的用戶退出流程 如果用戶在論壇點擊「退出」，則轉向到事先設置好的應用程序退出頁面（http://www.mywebsite.com/login.php?action=logout），並在登錄頁面 的 URL 中加入參數 forward（例如 http://www.mywebsite.com/login.php?action=login&forward=http://www.myforums.com/index.php），用於 在退出後將用戶導向到指定的 URL。 應用程序收到此請求後，清除自身 Cookie 或 Session，使得應用程序自身處於非登錄狀態。 檢查是否提交了 forward 變量，如有，則意味著登錄請求可能是由論壇而來，將此變量傳遞到後面的請求中。如沒有，自行生成 forward 變量，使得論壇登錄後能夠跳轉回到應用程序中。 通過 header('Location: http://www.myforums.com/api/passport.php?action=logout&forward=http://yyy&verify=zzz') 的方式，將 退出請求傳遞到論壇進行處理。其中 forward 用於告知論壇 Passport API 完成自身操作後轉向到的 URL 地址，verify 用於驗證 forward 變量的 有效性。forward、verify 格式與結構將在後面進行說明。 Discuz! Passport API 在接收到由應用程序通過 header() 提交過來的請求後，進行如下操作： 根據 verify 判斷 forward 變量是否合法，如合法則繼續，否則終止。 清楚論壇的 Cookie 或 Session，使得論壇自身處於非登錄狀態。 根據應用程序反饋的 forward 值，通過 header('Location: http://xxx') 的形式將頁面跳轉到 forward 變量指定的 URL。 至此，退出流程結束。 開啟通行證後的用戶註冊流程 如果用戶在論壇點擊「註冊」，則轉向到事先設置好的應用程序註冊頁面（http://www.mywebsite.com/register.php），並在註冊頁面的 URL 中 加入參數 forward（例如 http://www.mywebsite.com/register.php?forward=http://www.myforums.com/index.php），用於在註冊後將用戶導向到 指定的 URL 應用程序收到此請求後，按照慣例生成表單，並增加一個表單變量 ， 將 GET 方式傳遞過來的 forward 參數通過表單進行傳遞 用戶在應用程序的表單中填寫註冊信息，並提交到應用程序的註冊驗證程序。應用程序驗證用戶提交信息的完整性和合法性： 如果不通過：提示其問題所在，要求其返回上一頁重新填寫 如果通過，需要進行如下操作： 將用戶資料插入到應用程序自身用戶數據庫中 設置自身 Cookie 或 Session，使得應用程序自身處於登錄狀態 檢查表單中是否提交了 forward 變量，如有，則意味著註冊請求可能是由論壇而來，將此變量傳遞到後面的請求中。如沒有，自行生成 forward 變量，使得論壇註冊後能夠跳轉回到應用程序中 通過 header('Location: http://www.myforums.com/api/passport.php?action=login&auth=xxx&forward=http://yyy&verify=zzz') 的 方式，將註冊請求傳遞到論壇進行處理。其中 auth 用來將用戶信息與資料以特定的格式，加密傳遞給論壇，forward 用於告知論壇 Passport API 完成 自身操作後轉向到的 URL 地址，verify 用於驗證前面兩個變量的有效性。auth、forward、verify 格式與結構將在後面進行說明 Discuz! Passport API 在接收到由應用程序通過 header() 提交過來的請求後，進行如下操作： 根據 verify 判斷 auth 和 forward 變量是否合法，如合法則繼續，否則終止 將 auth 根據既定算法解密，並還原成數組，數組的內容與格式將在後面進行說明。根據數組中的內容，檢查此用戶是否存在。如存在，則根據 上述數組中的內容 UPDATE 論壇中相應的用戶資料。如不存在，則使用數組中的信息 INSERT 到論壇用戶資料表中 論壇設置 Cookie 或 Session，使得論壇自身處於登錄狀態 根據應用程序反饋的 forward 值，通過 header('Location: http://xxx') 的形式將頁面跳轉到 forward 變量指定的 URL 至此，註冊流程結束 本部分中，加下劃線顯示的部分，是需要對您的應用程序進行更改的部分，事實上，這部分更改會非常容易和方便。
Discuz! Passport 參數規格與加密方式
私有密匙(passport_key) 由於一些關鍵參數採用了 GET 方式進行傳遞，即便兩次 header 跳轉並不會直接將鏈接顯示在外面，但我們仍然對關鍵的參數進行了加密，私有 密匙共有兩個作用：其一是供下面提到的可逆加密算法（AzDGCrypt）進行數據的加解密。其二是生成不可逆驗證字串（verify），以防止關鍵信息被 偽造。 在啟用 Discuz! Passort 後，您需要在應用程序和 Discuz! 後台配置兩處私有密匙，這兩處的內容必須完全相同，這樣應用程序和論壇之間才能 正常通信。私有密匙決定了加密算法的強度，因此密匙長度請不要小於 10 個字節，並包含字母、數字和符號，以保證系統的安全。 加密算法 Discuz! Passport 採用 Azerbaijan Development Group（AzDG）開發的可逆加密算法 AzDGCrypt 對用戶資料進行加密。如提供正確的私有密匙， 可通過本加密算法對數據進行加密及解密，因此只要保證私有密匙的保密性，即可確保數據傳遞過程中的安全。以下為 Discuz! Passport 中應用到 的可逆加密算法，為了生成可以被 Discuz! Passport 正確解密的 auth 字串，需要將如下函數放置於應用程序中，並可在登錄及註冊時調用。 passport_encrypt()是加密函數，用法為 passport_encrypt($txt, $key)，其中 $txt 是待加密的字串，$key 是私有密匙。 passport_decrypt()是解密函數，用法為 passport_decrypt($txt, $key)，其中 $txt 是加密後的字串，$key 是私有密匙。
    	/**
    	* Passport 加密函數
    	*
    	* @param		string		等待加密的原字串
    	* @param		string		私有密匙(用於解密和加密)
    	*
    	* @return	string		原字串經過私有密匙加密後的結果
    	*/
    	function passport_encrypt($txt, $key) {

    		// 使用隨機數發生器產生 0~32000 的值並 MD5()
    		srand((double)microtime() * 1000000);
    		$encrypt_key = md5(rand(0, 32000));

    		// 變量初始化
    		$ctr = 0;
    		$tmp = '';

    		// for 循環，$i 為從 0 開始，到小於 $txt 字串長度的整數
    		for($i = 0; $i < strlen($txt); $i++) {
    			// 如果 $ctr = $encrypt_key 的長度，則 $ctr 清零
    			$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
    			// $tmp 字串在末尾增加兩位，其第一位內容為 $encrypt_key 的第 $ctr 位，
    			// 第二位內容為 $txt 的第 $i 位與 $encrypt_key 的 $ctr 位取異或。然後 $ctr = $ctr + 1
    			$tmp .= $encrypt_key[$ctr].($txt[$i] ^ $encrypt_key[$ctr++]);
    		}

    		// 返回結果，結果為 passport_key() 函數返回值的 base65 編碼結果
    		return base64_encode(passport_key($tmp, $key));

    	}

    	/**
    	* Passport 解密函數
    	*
    	* @param		string		加密後的字串
    	* @param		string		私有密匙(用於解密和加密)
    	*
    	* @return	string		字串經過私有密匙解密後的結果
    	*/
    	function passport_decrypt($txt, $key) {

    		// $txt 的結果為加密後的字串經過 base64 解碼，然後與私有密匙一起，
    		// 經過 passport_key() 函數處理後的返回值
    		$txt = passport_key(base64_decode($txt), $key);

    		// 變量初始化
    		$tmp = '';

    		// for 循環，$i 為從 0 開始，到小於 $txt 字串長度的整數
    		for ($i = 0; $i < strlen($txt); $i++) {
    			// $tmp 字串在末尾增加一位，其內容為 $txt 的第 $i 位，
    			// 與 $txt 的第 $i + 1 位取異或。然後 $i = $i + 1
    			$tmp .= $txt[$i] ^ $txt[++$i];
    		}

    		// 返回 $tmp 的值作為結果
    		return $tmp;

    	}

    	/**
    	* Passport 密匙處理函數
    	*
    	* @param		string		待加密或待解密的字串
    	* @param		string		私有密匙(用於解密和加密)
    	*
    	* @return	string		處理後的密匙
    	*/
    	function passport_key($txt, $encrypt_key) {

    		// 將 $encrypt_key 賦為 $encrypt_key 經 md5() 後的值
    		$encrypt_key = md5($encrypt_key);

    		// 變量初始化
    		$ctr = 0;
    		$tmp = '';

    		// for 循環，$i 為從 0 開始，到小於 $txt 字串長度的整數
    		for($i = 0; $i < strlen($txt); $i++) {
    			// 如果 $ctr = $encrypt_key 的長度，則 $ctr 清零
    			$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
    			// $tmp 字串在末尾增加一位，其內容為 $txt 的第 $i 位，
    			// 與 $encrypt_key 的第 $ctr + 1 位取異或。然後 $ctr = $ctr + 1
    			$tmp .= $txt[$i] ^ $encrypt_key[$ctr++];
    		}

    		// 返回 $tmp 的值作為結果
    		return $tmp;

    	}

    	/**
    	* Passport 信息(數組)編碼函數
    	*
    	* @param		array		待編碼的數組
    	*
    	* @return	string		數組經編碼後的字串
    	*/
    	function passport_encode($array) {

    		// 數組變量初始化
    		$arrayenc = array();

    		// 遍歷數組 $array，其中 $key 為當前元素的下標，$val 為其對應的值
    		foreach($array as $key => $val) {
    			// $arrayenc 數組增加一個元素，其內容為 "$key=經過 urlencode() 後的 $val 值"
    			$arrayenc[] = $key.'='.urlencode($val);
    		}

    		// 返回以 "&" 連接的 $arrayenc 的值(implode)，例如 $arrayenc = array('aa', 'bb', 'cc', 'dd')，
    		// 則 implode('&', $arrayenc) 後的結果為 」aa&bb&cc&dd"
    		return implode('&', $arrayenc);

    	}
passport_encode()是將數組轉換合成為字串形式存儲的函數：變量名和數值之間用等號連接，如果數值包含特殊字符，使用 urlencode() 將其轉碼。 多個變量間使用 & 分割。例如原始數組內容為 array('username' => 'abc', 'email' => 'my+discuz@gmail.com')，經過 passport_encode() 編碼後 結果為 username=abc&email=my%2Bdiscuz%40gmail.com。 信息字串(auth) 應用程序在收到登錄或註冊請求，並讀取到用戶資料後，請按如下的要求將用戶資料及部分其他信息存放於一個數組之中。數組各鍵值的含義為： cookietime 應用程序保存該用戶登錄記錄的時間，可為非負整數，單位秒，Discuz! Passport 收到此參數後，會設置同樣的 Cookie 過期時間，這樣應用 程序和論壇可以保證同樣的登錄有效性。如不傳遞此參數，或參數數值不正確，則 Discuz! Passport 按照 0 設置 Cookie 有效期。 time 應用程序所在服務器當前時間（9 或 10 位數字 Unix Timestamp），此參數用於 Discuz! 所在服務器當前時間進行比對，如果早於當前時間 超過若干秒（取決於 Discuz! Passport 中的「驗證字串有效期」設定），則視為本 auth 內容無效，避免此URL被人得知後可能的安全問題。 username 用戶登錄或註冊的用戶名。Discuz! 的註冊用戶名規則為： 長度 1~15 個字符，不得為空 不得為 c:\con\con、遊客(gb2312 或 big5 內碼)、Guest 不得包含 (,)、(*)、(")、([TAB])、([SPACE])、([\r])、([\n])、(<)、(>)、(&)其中之一 如果應用程序提交過來的用戶名不符合上述規則，Passport 將自動去處其中的特殊字符，將過濾後的結果寫入數據庫中。 password 用戶密碼經 MD5 不可逆加密後的值。如果此密碼使用非 MD5 加密，則應用程序和 Passport 不能正常關聯和使用。 email 用戶 Email 地址（50 個字節以內）。 isadmin 當前用戶是否是應用程序的最高管理員，1=是，0=否。最高管理員的權限，將同步到論壇中去，其他下級管理員的身份將不進行同步，而由最高 管理員分別在不同的系統中進行設置。 credits 當前用戶在應用程序中的積分值，範圍 -2147483648 到 2147483647，如果 Discuz! Passport 中設置了目標積分項，則用戶登錄時，Passport 會把應用程序傳遞過來的 credits 值同步到指定的論壇的指定積分項目中 gender 當前用戶的性別，1=男，2=女，0=未知。 bday 當前用戶的生日，格式 yyyy-mm-dd。 regip 當前用戶註冊時的 IP 地址。 regdate 當前用戶註冊的時間（9 或 10 位數字 Unix Timestamp）。 nickname 當前用戶的暱稱（30 個字節以內，如傳遞此參數，必須打開相應用戶組的暱稱權限，否則用戶在控制面板中提交個人資料時，會導致暱稱失效）。 site 當前用戶的主頁地址（包含http://）。 qq 當前用戶的 QQ 號碼。 ICQ 當前用戶的 ICQ 賬號。 msn 當前用戶的 MSN Messenger 賬號。 yahoo 當前用戶的 Yahoo! Messanger 賬號。 以上參數中，以黑體下劃線顯示的 time、username、password、email 是必須傳遞的參數，缺少上述參數 Passport 將無法正常工作。其他的參數是可選的，如果 不傳遞某些參數，則 Passport 會進行識別，自動不更新沒有傳遞的參數所在的字段。所有數值，請提供原始值，而非經過反斜線轉義（addslashes）後 的結果。 把上述信息存放於數組中，假定為如下的形式：
    	$member = array
    		(
    		'cookietime'	=> 31536000,
    		'time'		=> 1117415922,
    		'username'	=> 'Abcd',
    		'password'	=> 'e2fc714c4727ee9395f324cd2e7f331f',
    		'email'		=> 'abcd@efgh.com',
    		'credits'	=> 123,
    		'regip'		=> '210.120.222.111',
    		'regdate'	=> '1012752000',
    		'msn'		=> 'email@hotmail.com'
    		);
將其經過如下的加密變換，即可得到 auth 的值：
    	$auth = passport_encrypt(passport_encode($member), $passport_key);
其中，passport_encode() 在前文已做了說明，用於將數組內容存放於特定的格式，$passport_key 是私有密匙。 切記：由於 $auth 中可能含有等號、加號等特殊字符，請將 $auth 經過 rawurlencode() 編碼後再在 URL 中傳遞，否則可能會產生問題。 導向字串(forward) 導向字串用於通知 Discuz! Passport 在完成自身操作後，返回到哪一個 URL 地址，例如 http://www.myforums.com/forumdisplay.php?fid=2。 如果 forward 為空，則默認導向到應用程序的首頁 切記：由於 $forward 中可能含有冒號、問號、等號等特殊字符，請將 $forward 經過 rawurlencode() 編碼後再在 URL 中傳遞，否則可能會 產生問題。 驗證字串(verify) 驗證字串用戶檢驗 auth 和 forward 兩個參數的合法性，避免非法構造參數進行破壞的可能。無論 auth 和 forward 變量是否存在，驗證字串 （verify）的值均為：
    	$verify = md5($action.$auth.$forward.$passport_key);
其中，$action 是當前執行的 Passport 操作，如 login 等等；$auth 是用戶信息加密後，並經 rawurlencode() 之前的內容。$forward 是經 rawurlencode() 前的導向字串、$passport_key 是私有密匙。如果 verify 的值不匹配，則 Passport 拒絕進行下一步操作。
Discuz! Passport 設置與啟用
內置關聯 Discuz! 以戰略合作的方式，與業內知名的產品實現了 Passport 關聯，目前內置了 SiteEngine 建站引擎（http://www.siteengine.net）和 Shopex 通用型網上商店系統（http://www.shopex.cn）的相關接口，這樣用戶只須透過在兩套軟件中簡單的設置，即可開啟這些關聯。 其他應用程序 由於 Discuz! Passport 的高可擴展性和平台無關性，使得您可以參照前文的說明，稍稍改動小部分的代碼，便將任何 B/S 模式的應用程序與 Discuz! 進行關聯。 參數設置 您可以在 Discuz! 系統設置中，看到相應的通行證設置功能，在 Discuz! 合作夥伴的軟件中，也可以找到這些設置入口。相關的操作已比較簡單， 在此不再詳細敘述。 特別說明 如果您先運營了論壇，後與其他應用程序啟用了 Passport 關聯，由於之前論壇中的用戶數據沒有同步，您需要先寫一個導入程序，將論壇的用戶 數據導入到應用程序的用戶表中，否則以往在論壇註冊的用戶將無法通過 Passport 登錄。已成功關聯後新註冊的用戶無此問題。 在開啟了 Discuz! 通行證後，您仍然可以通過 logging.php?action=login 這個鏈接來登錄論壇，以備調試之用，但頁面上顯示的鏈接將改為應用程序 的登錄 URL。注意：開啟通行證後，建議您通過 Discuz! 選項關閉論壇本身的註冊功能，以免用戶通過論壇註冊而產生無法同步的問題。 您可以在 Discuz! 的 api/passport.php 找到 Discuz! Passport 的全部源程序，您也許通過他更好的理解 Passport 的原理，更快的完成應用程序與 Discuz! 之間的整合。
典型錯誤提示
Illegal request 非法請求，當驗證字串 verify 不匹配時會產生此提示。可能是應用程序與 Discuz! 配置的私有密匙不同，或是通過 URL 傳遞前，未將必要的參數 （如 auth、forward 等）進行 URL 編碼，也有可能是使用了經過 URL 編碼的參數值用來計算 verify 的 md5 值造成。以 PHP 語言為例，正確的代碼 應當是類似於的如下的格式：
    	$action		= 'login';
    	$auth		= passport_encrypt(passport_encode($autharray), $passport_key);
    	$forward		= 'http://www.discuz.net/index.php';
    	$verify		= md5($action.$auth.$forward.$passport_key);

    	header("Location: http://www.discuz.net/api/passport.php".
    		"?action=$action".
    		"&auth=".rawurlencode($auth).
    		"&forward=".rawurlencode($forward).
    		"&verify=$verify");
Lack of required parameters auth 內容解密後，缺少必要的信息 time、username、password、email。 Request expired 請求過期。當前服務器時間與應用程序提交過來的 time 之差大於 Discuz! Passport 中設置的請求有效期。可能是使用以往的代碼非法嘗試，也可能是 由於應用程序和 Discuz! 論壇所在的兩台服務器，時間設置有誤造成。 Invalid action 沒有指定 Passport 所執行的 action。