AGAMES 討論區

 找回密碼
 快速註冊
搜索
查看: 1536|回復: 0
打印 上一主題 下一主題

Discuz! Passport 接口技術文檔 [複製鏈接]

七級帝王會員

☆Prince★}+}|電腦王子

Rank: 7Rank: 7Rank: 7

帖子
3799
EXP
6040 點
金幣
4990 個
好友
0
註冊時間
2008-3-27

友誼之星勳章

跳轉到指定樓層
樓主
發表於 2009-1-16 16:01:44 |只看該作者 |倒序瀏覽
Discuz! Passport 接口技術文檔從 Discuz! 4.0.0 RC4 版本開始,Discuz! 內嵌了一個獨特的 Passport(通行證) 接口,利用此接口,用戶將很容易將論壇與其他應用程序整合,而實現統一登錄與退出、用戶數據共享、積分同步等功能。可以整合的應用程序包括內容管理系統(CMS)、商城系統、游戲系統等等,如您對這方面功能有興趣或有需求,請繼續閱讀本文檔。
Discuz! Passport 的優點Discuz! Passport 系統使用了 Discuz! 獨有的技術,並不等同於以往使用過的一些方法,與傳統的實現方式相比,具備(不限於)以下優勢:
  • 基於私有密匙的低相關性可逆加密算法,配合 MD5 校檢碼技術,使得暴力破解或偽造幾乎不可能。
  • 應用程序可與論壇放置於不同的服務器及不同的域名下。可基於不同操作系統、不同程序語言和不同數據庫平台,具備真正的平台無關性。
  • 不需要任何形式的數據庫連接、或強制把兩套應用程序的數據放在同一數據庫甚至同一數據表中。論壇與應用程序都有各自的用戶數據表,只是在需要時進行無縫同步操作。
  • 對應用程序的代碼改動簡便易行,可最快速的完成應用程序與論壇間的整合。
Discuz! Passport 的局限您在開始利用 Discuz! Passport 進行二次開發時,需要了解這個系統的局限性,以對未來的工作進行正確的評估與安排。
  • 只能工作在用戶密碼不加密、可逆加密或 MD5 加密的情況下,否則論壇後台無法登錄。
  • 只能與一種應用程序關聯,即二方關聯。不能實現三方關聯或與更多的應用程序進行關聯。
  • 應用程序需具有獨立的註冊、登錄、退出頁面和鏈接,否則需要自行修改論壇中的相應表單或程序。
  • 由於論壇的註冊人數可能很多,例如百萬級以上,且應用程序和論壇間的用戶數據是同步的,因此要求應用程序能夠穩定的負載大量用戶的訪問。
原理與流程假設已設置如下變量或參數開啟通行證後的用戶登錄流程
  • 如果用戶在論壇點擊“登錄”,則轉向到事先設置好的應用程序登錄頁面(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/registe ... orums.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 是私有密匙。

  1. /**
  2. * Passport 加密函數
  3. *
  4. * @param  string  等待加密的原字串
  5. * @param  string  私有密匙(用於解密和加密)
  6. *
  7. * @return string  原字串經過私有密匙加密後的結果
  8. */
  9. function passport_encrypt($txt, $key) {
  10.   // 使用隨機數發生器產生 0~32000 的值並 MD5()
  11.   srand((double)microtime() * 1000000);
  12.   $encrypt_key = md5(rand(0, 32000));
  13.   // 變量初始化
  14.   $ctr = 0;
  15.   $tmp = '';
  16.   // for 循環,$i 為從 0 開始,到小於 $txt 字串長度的整數
  17.   for($i = 0; $i < strlen($txt); $i++) {
  18.    // 如果 $ctr = $encrypt_key 的長度,則 $ctr 清零
  19.    $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
  20.    // $tmp 字串在末尾增加兩位,其第一位內容為 $encrypt_key 的第 $ctr 位,
  21.    // 第二位內容為 $txt 的第 $i 位與 $encrypt_key 的 $ctr 位取異或。然後 $ctr = $ctr + 1
  22.    $tmp .= $encrypt_key[$ctr].($txt[$i] ^ $encrypt_key[$ctr++]);
  23.   }
  24.   // 返回結果,結果為 passport_key() 函數返回值的 base64 編碼結果
  25.   return base64_encode(passport_key($tmp, $key));
  26. }
  27. /**
  28. * Passport 解密函數
  29. *
  30. * @param  string  加密後的字串
  31. * @param  string  私有密匙(用於解密和加密)
  32. *
  33. * @return string  字串經過私有密匙解密後的結果
  34. */
  35. function passport_decrypt($txt, $key) {
  36.   // $txt 的結果為加密後的字串經過 base64 解碼,然後與私有密匙一起,
  37.   // 經過 passport_key() 函數處理後的返回值
  38.   $txt = passport_key(base64_decode($txt), $key);
  39.   // 變量初始化
  40.   $tmp = '';
  41.   // for 循環,$i 為從 0 開始,到小於 $txt 字串長度的整數
  42.   for ($i = 0; $i < strlen($txt); $i++) {
  43.    // $tmp 字串在末尾增加一位,其內容為 $txt 的第 $i 位,
  44.    // 與 $txt 的第 $i + 1 位取異或。然後 $i = $i + 1
  45.    $tmp .= $txt[$i] ^ $txt[++$i];
  46.   }
  47.   // 返回 $tmp 的值作為結果
  48.   return $tmp;
  49. }
  50. /**
  51. * Passport 密匙處理函數
  52. *
  53. * @param  string  待加密或待解密的字串
  54. * @param  string  私有密匙(用於解密和加密)
  55. *
  56. * @return string  處理後的密匙
  57. */
  58. function passport_key($txt, $encrypt_key) {
  59.   // 將 $encrypt_key 賦為 $encrypt_key 經 md5() 後的值
  60.   $encrypt_key = md5($encrypt_key);
  61.   // 變量初始化
  62.   $ctr = 0;
  63.   $tmp = '';
  64.   // for 循環,$i 為從 0 開始,到小於 $txt 字串長度的整數
  65.   for($i = 0; $i < strlen($txt); $i++) {
  66.    // 如果 $ctr = $encrypt_key 的長度,則 $ctr 清零
  67.    $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
  68.    // $tmp 字串在末尾增加一位,其內容為 $txt 的第 $i 位,
  69.    // 與 $encrypt_key 的第 $ctr + 1 位取異或。然後 $ctr = $ctr + 1
  70.    $tmp .= $txt[$i] ^ $encrypt_key[$ctr++];
  71.   }
  72.   // 返回 $tmp 的值作為結果
  73.   return $tmp;
  74. }
  75. /**
  76. * Passport 信息(數組)編碼函數
  77. *
  78. * @param  array  待編碼的數組
  79. *
  80. * @return string  數組經編碼後的字串
  81. */
  82. function passport_encode($array) {
  83.   // 數組變量初始化
  84.   $arrayenc = array();
  85.   // 遍歷數組 $array,其中 $key 為當前元素的下標,$val 為其對應的值
  86.   foreach($array as $key => $val) {
  87.    // $arrayenc 數組增加一個元素,其內容為 "$key=經過 urlencode() 後的 $val 值"
  88.    $arrayenc[] = $key.'='.urlencode($val);
  89.   }
  90.   // 返回以 "&" 連接的 $arrayenc 的值(implode),例如 $arrayenc = array('aa', 'bb', 'cc', 'dd'),
  91.   // 則 implode('&', $arrayenc) 後的結果為 ”aa&bb&cc&dd"
  92.   return implode('&', $arrayenc);
  93. }
複製代碼


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)後的結果。
把上述信息存放於數組中,假定為如下的形式:

  1. $member = array
  2.   (
  3.   'cookietime' => 31536000,
  4.   'time'  => 1117415922,
  5.   'username' => 'Abcd',
  6.   'password' => 'e2fc714c4727ee9395f324cd2e7f331f',
  7.   'email'  => 'abcd@efgh.com',
  8.   'credits' => 123,
  9.   'regip'  => '210.120.222.111',
  10.   'regdate' => '1012752000',
  11.   'msn'  => 'email@hotmail.com'
  12.   );
複製代碼


將其經過如下的加密變換,即可得到 auth 的值:


  1. $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)的值均為:


  1. $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 語言為例,正確的代碼應當是類似於的如下的格式:

  1. $action  = 'login';
  2. $auth  = passport_encrypt(passport_encode($autharray), $passport_key);
  3. $forward  = 'http://www.discuz.net/index.php';
  4. $verify  = md5($action.$auth.$forward.$passport_key);
  5. header("Location: http://www.discuz.net/api/passport.php".
  6. "?action=$action".
  7. "&auth=".rawurlencode($auth).
  8. "&forward=".rawurlencode($forward).
  9. "&verify=$verify");
複製代碼

Lack of required parametersauth 內容解密後,缺少必要的信息 time、username、password、email。
Request expired請求過期。當前服務器時間與應用程序提交過來的 time 之差大於 Discuz! Passport 中設置的請求有效期。可能是使用以往的代碼非法嘗試,也可能是由於應用程序和 Discuz! 論壇所在的兩台服務器,時間設置有誤造成。
Invalid action沒有指定 Passport 所執行的 action。
Alta Multimedia - Youtube:http://www.youtube.com/user/altahk
您需要登錄後才可以回帖 登錄 | 快速註冊

Archiver|手機版|AGAMES 討論區

GMT+8, 2024-11-26 21:29 , Processed in 0.047482 second(s), 11 queries .

Powered by Discuz! X2

© 2001-2011 Comsenz Inc.

回頂部