Discuz! Passport的SSO接口技術文檔(轉)

轉載自: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

用戶 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。

相關文章
相關標籤/搜索