相關(guān)關(guān)鍵詞
關(guān)于我們
最新文章
PHP版單點登陸實現(xiàn)方案的實例
摘要:
本文主要介紹了利用webservice,session,cookie技術(shù),來進行通用的單點登錄系統(tǒng)的分析與設(shè)計。具體實現(xiàn)語言為PHP。單點 登錄,英文名為Single Sign On,簡稱為 SSO,是目前企業(yè),網(wǎng)絡(luò)業(yè)務(wù)的用戶綜合處理的重要組成部分。而SSO的定義,是在多個應(yīng)用系統(tǒng)中,用戶只需要登陸一次就可以訪問所有相互信任的應(yīng)用系 統(tǒng)。
動機:
用過ucenter的全站登錄方式的朋友,應(yīng)該都知道這是典型的觀察者模式的解決方案。用戶中心作為subject, 其所屬observer的注冊和刪除統(tǒng)一在ucenter的后臺進行。而各個子應(yīng)用站點都對應(yīng)一個observer。每次用戶中心的登錄動作,都會觸發(fā) js腳本回調(diào)w3c標準的子站登錄接口(api/uc.php)。
這種方式的缺點,本人認為主要是兩點:1. 子站點過多時,回調(diào)接口相應(yīng)增多,這個在分布子站的量的限制上,如何控制來使登錄效率不會太低,不好把握; 2. 當某個子站回調(diào)接口出現(xiàn)問題時,默認的登錄過程會卡住(可以限制登錄程序的執(zhí)行時間,但相應(yīng)出現(xiàn)問題子站后面的子站的回調(diào)接口就調(diào)不到了。
基于以上問題,在實際開發(fā)過程中,本人設(shè)計了另一套單點登錄系統(tǒng)。
一. 登陸原理說明
單點登錄的技術(shù)實現(xiàn)機制:當用戶第一次訪問應(yīng)用系統(tǒng)1的時候,因為還沒有登錄,會被引導(dǎo)到認證系統(tǒng)中進行登錄;根據(jù)用戶提供的登錄信息,認證系統(tǒng)進行身份效驗,如果通過效驗,應(yīng)該返回給用戶一個認證的憑據(jù)--ticket;用戶再訪問別的應(yīng)用的時候,就會將這個ticket帶上,作為自己認證的憑據(jù),應(yīng)用系統(tǒng)接受到請求之后會把ticket送到認證系統(tǒng)進行效驗,檢查ticket的合法性。如果通過效驗,用戶就可以在不用再次登錄的情況下訪問應(yīng)用系統(tǒng)2和應(yīng)用系統(tǒng)3了。
可以看出,要實現(xiàn)SSO,需要以下主要的功能:
a) 所有應(yīng)用系統(tǒng)共享一個身份認證系統(tǒng);
b) 所有應(yīng)用系統(tǒng)能夠識別和提取ticket信息;
c) 應(yīng)用系統(tǒng)能夠識別已經(jīng)登錄過的用戶,能自動判斷當前用戶是否登錄過,從而完成單點登錄的功能
基于以上基本原則,本人用php語言設(shè)計了一套單點登錄系統(tǒng)的程序,目前已投入正式生成服務(wù)器運行。本系統(tǒng)程序,將ticket信息以全系統(tǒng)唯一的 session id作為媒介,從而獲取當前在線用戶的全站信息(登陸狀態(tài)信息及其他需要處理的用戶全站信息)。
二. 過程說明:
登陸流程:
1. 第一次登陸某個站:
a) 用戶輸入用戶名+密碼,向用戶驗證中心發(fā)送登錄請求
b) 當前登錄站點,通過webservice請求,用戶驗證中心驗證用戶名,密碼的合法性。如果驗證通過,則生成ticket,用于標識當前會話的用戶,并將當前登陸子站的站點標識符記錄到用戶中心,最后
c) 將獲取的用戶數(shù)據(jù)和ticket返回給子站。如果驗證不通過,則返回相應(yīng)的錯誤狀態(tài)碼。
d) 根據(jù)上一步的webservice請求返回的結(jié)果,當前子站對用戶進行登陸處理:如狀態(tài)碼表示成功的話,則當前站點通過本站cookie保存ticket,并本站記錄用戶的登錄狀態(tài)。狀態(tài)碼表示失敗的話,則給用戶相應(yīng)的登錄失敗提示。
2. 登陸狀態(tài)下,用戶轉(zhuǎn)到另一子:
a) 通過本站cookie或session驗證用戶的登錄狀態(tài):如驗證通過,進入正常本站處理程序;否則戶中心驗證用戶的登錄狀態(tài)(發(fā)送ticket到用戶驗證中心),如驗證通過,則對返回的用戶信息進行本地的登錄處理,否則表明用戶未登錄。
登出流程
a) 當前登出站清除用戶本站的登錄狀態(tài) 和 本地保存的用戶全站唯一的隨機id
b) 通過webservice接口,清除全站記錄的全站唯一的隨機id。webservice接口會返回,登出其他已登錄子站的javascript代碼,本站輸出此代碼。
c) js代碼訪問相應(yīng)站W(wǎng)3C標準的登出腳本
三. 代碼說明:
本文所涉及到相關(guān)代碼,已打包上傳,如有興趣,可在本文最后下載鏈接處點擊下載。
1. 登陸流程:
用戶從打開瀏覽器開始,第一個登陸的子站點,必須調(diào)用UClientSSO::loginSSO()方法。該方法返回全站唯一的隨機id用于標識該用戶。該隨機id在UClientSSO::loginSSO()中已通過本站cookie保存,即該子站點保留了用戶已登陸標識的存根于本站。
a) UClientSSO::loginSSO()方法如下:
<?php /** * 用戶驗證中心 登陸用戶處理 * * @param string $username - 用戶名 * @param string $password - 用戶原始密碼 * @param boolean $remember - 是否永久記住登陸賬號 * @param boolean $alreadyEnc - 傳入的密碼是否已經(jīng)經(jīng)過simpleEncPass加密過 * * @return array - integer $return['status'] 大于 0:返回用戶 ID,表示用戶登錄成功 * -1:用戶不存在,或者被刪除 * -2:密碼錯 * -11:驗證碼錯誤 * string $return['username'] : 用戶名 * string $return['password'] : 密碼 * string $return['email'] : Email */ static public function loginSSO($username, $password, $remember=false, $alreadyEnc=false) { self::_init(); self::_removeLocalSid(); $ret = array(); // //1. 處理傳入webservice接口的參數(shù) // $_params = array( 'username' => $username, 'password' => $alreadyEnc ? trim($password) : self::simpleEncPass(trim($password)), 'ip' => self::onlineip(), 'siteFlag' => self::$site, 'remember' => $remember ); $_params['checksum'] = self::_getCheckSum($_params['username'] . $_params['password'] . $_params['ip'] . $_params['siteFlag'] . $_params['remember']); // // 2.調(diào)用webservice接口,進行登陸處理 // $aRet = self::_callSoap('loginUCenter', $_params); if (intval($aRet['resultFlag']) > 0 && $aRet['sessID']) { //成功登陸 //設(shè)置本地session id self::_setLocalSid($aRet['sessID']); //設(shè)置用戶中心的統(tǒng)一session id腳本路徑 self::$_synloginScript = urldecode($aRet['script']); $ret = $aRet['userinfo']; } else { $ret['status'] = $aRet['resultFlag']; } return $ret; }//end of function //b) 用戶驗證中心的webservice服務(wù)程序,接收到登陸驗證請求后,調(diào)用UCenter::loginUCenter()方法來處理登陸請求。 /** * 用戶驗證中心 登陸用戶處理 * * @param string $username * @param string $password * @param string $ip * @param string $checksum * @return array */ static public function loginUCenter($username, $password, $ip, $siteFlag, $remember=false) { self::_init(); session_start(); $ret = array(); $arr_login_res = login_user($username, $password, $ip); $res_login = $arr_login_res['status']; // $ret['resultFlag'] = $res_login; if ($res_login < 1) { //登陸失敗 } else { //登陸成功 $_SESSION[self::$_ucSessKey] = $arr_login_res; $_SESSION[self::$_ucSessKey]['salt'] = self::_getUserPassSalt($_SESSION[self::$_ucSessKey]['username'], $_SESSION[self::$_ucSessKey]['password']); $ret['userinfo'] = $_SESSION[self::$_ucSessKey]; $ret['sessID'] = session_id(); //生成全站的唯一session id,作為ticket全站通行 // //合作中心站回調(diào)登陸接口(設(shè)置用戶中心的統(tǒng)一session id) // self::_createCoSitesInfo(); $uinfo = array(); $_timestamp = time(); $_rawCode = array( 'action' => 'setSid', 'sid' => $ret['sessID'], 'time' => $_timestamp, ); if ($remember) { $uinfo = array( 'remember' => 1, 'username' => $username, 'password' => $password ); } $ret['script'] = ''; $_rawStr = http_build_query(array_merge($_rawCode, $uinfo)); // // 合作站點的全域cookie設(shè)置腳本地址 // foreach ((array)self::$_coSitesInfo as $_siteInfo) { $_code = self::authcode($_rawStr, 'ENCODE', $_siteInfo['key']); $_src = $_siteInfo['url'] . '?code=' . $_code . '&time=' . $_timestamp; $ret['script'] .= urlencode(''); } // // 記住已登陸戰(zhàn) // self::registerLoggedSite($siteFlag, $ret['sessID']); unset($ret['userinfo']['salt']); } return $ret; } ?>