轉載自:http://hi.baidu.com/mmpritty/item/c6f69d40d2c85990823ae133php
從 Discuz! 4.0.0 RC4 版本開始,Discuz! 內嵌了一個獨特的 Passport(通行證) 接口,利用此接口,用戶將很 容易將論壇與其餘應用程序整合,而實現統一登陸與退出、用戶數據共享、積分同步等功能。能夠整合的應用程序包括內容管理系統(CMS)、商城系統、遊戲系 統等等,如您對這方面功能有興趣或有需求,請繼續閱讀本文檔。web
Discuz! Passport 的優勢算法
Discuz! Passport 系統使用了 Discuz! 獨有的技術,並不等同於以往使用過的一些方法,與傳統的實現方式相比,具有(不限於)如下優點:數據庫
· 基於私有密匙的低相關性可逆加密算法,配合 MD5 校檢碼技術,使得暴力破解或僞造幾乎不可能。api
· 應用程序可與論壇放置於不一樣的服務器及不一樣的域名下。可基於不一樣操做系統、不一樣程序語言和不一樣數據庫平臺,具有真正的平臺無關性。數組
· 不須要任何形式的數據庫鏈接、或強制把兩套應用程序的數據放在同一數據庫甚至同一數據表中。論壇與應用程序都有各自的用戶數據表,只是在須要時進行無縫同步操做。安全
· 對應用程序的代碼改動簡便易行,可最快速的完成應用程序與論壇間的整合。服務器
Discuz! Passport 的侷限cookie
您在開始利用 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 參數經過表單進行傳遞。
· 用戶在應用程序的表單中填寫登陸信息,並提交到應用程序的登陸驗證程序。應用程序驗證用戶提交的用戶名和密碼的合法性:
o 若是不經過:提示用戶名密碼錯誤,要求其返回上一頁從新填寫。
o 若是經過,須要進行以下操做:
§ 設置自身 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() 提交過來的請求後,進行以下操做:
o 根據verify 判斷 auth 和 forward 變量是否合法,如合法則繼續,不然終止。
o 將 auth 根據既定算法解密,並還原成數組,數組的內容與格式將在後面進行說明。根據數組中的內容,檢查此用戶是否存在。如存在,則根據上述數組中的內容 UPDATE 論壇中相應的用戶資料。如不存在,則使用數組中的信息 INSERT 到論壇用戶資料表中。
o 論壇設置 Cookie 或 Session,使得論壇自身處於登陸狀態。
o 根據應用程序反饋的 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() 提交過來的請求後,進行以下操做:
o 根據 verify 判斷 forward 變量是否合法,如合法則繼續,不然終止。
o 清楚論壇的 Cookie 或 Session,使得論壇自身處於非登陸狀態。
o 根據應用程序反饋的 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 參數經過表單進行傳遞
· 用戶在應用程序的表單中填寫註冊信息,並提交到應用程序的註冊驗證程序。應用程序驗證用戶提交信息的完整性和合法性:
o 若是不經過:提示其問題所在,要求其返回上一頁從新填寫
o 若是經過,須要進行以下操做:
§ 將用戶資料插入到應用程序自身用戶數據庫中
§ 設置自身 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() 提交過來的請求後,進行以下操做:
o 根據 verify 判斷 auth 和 forward 變量是否合法,如合法則繼續,不然終止
o 將 auth 根據既定算法解密,並還原成數組,數組的內容與格式將在後面進行說明。根據數組中的內容,檢查此用戶是否存在。如存在,則根據上述數組中的內容 UPDATE 論壇中相應的用戶資料。如不存在,則使用數組中的信息 INSERT 到論壇用戶資料表中
o 論壇設置 Cookie 或 Session,使得論壇自身處於登陸狀態
o 根據應用程序反饋的 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() 函數返回值的 base64 編碼結果
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);
}
/**
* 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' =& gt; '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! 的註冊用戶名規則爲:
o 長度 1~15 個字符,不得爲空
o 不得爲 c:\con\con、遊客(gb2312 或 big5 內碼)、Guest
o 不得包含 (,)、(*)、(")、([TAB])、([SPACE])、([\r])、([\n])、(<)、(>)、(&)其中之一
若是應用程序提交過來的用戶名不符合上述規則,Passport 將自動去處其中的特殊字符,將過濾後的結果寫入數據庫中。
· password
用戶密碼經 MD5 不可逆加密後的值。若是此密碼使用非 MD5 加密,則應用程序和 Passport 不能正常關聯和使用。
用戶 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 號碼。
· 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。