首頁 後端開發 php教程 PHP版單點登陸實現方案的實例

PHP版單點登陸實現方案的實例

Feb 28, 2017 am 09:18 AM

摘要:

本文主要介紹了利用webservice,session,cookie技術,來進行通用的單一登入系統的分析與設計。具體實作語言為PHP。單點 登錄,英文名為Single Sign On,簡稱 SSO,是目前企業,網絡業務的用戶綜合處理的重要組成部分。而SSO的定義,是在多個應用系統中,使用者只需要登陸一次就可以存取所有互信的應用系 統。

動機:

用過ucenter的全站登入方式的朋友,應該都知道這是典型的觀察者模式的解決方案。用戶中心作為subject, 其所屬observer的註冊和刪除統一在ucenter的後台進行。而各個子應用程式站點都對應一個observer。每次使用者中心的登入動作,都會觸發 js腳本回呼w3c標準的子站登入介面(api/uc.php)。

這種方式的缺點,本人認為主要是兩點:1. 子站點過多時,回調接口相應增多,這個在分佈子站的量的限制上,如何控制來使登錄效率不會太低,不好把握; 2. 當某個子站回調介面出現問題時,預設的登入程序會卡住(可以限制登入程序的執行時間,但對應出現問題子站後面的子站的回呼介面就調不到了。
單點登錄的技術實現機制:當用戶第一次訪問應用系統1的時候,因為還沒有登錄,會被引導到認證系統中進行登錄;根據用戶提供的登錄信息,認證系統進行身份效驗,如果通過效驗,應該返回給用戶一個認證的憑證--ticket;用戶再訪問別的應用的時候,就會將這個ticket帶上,作為自己認證的憑據,應用系統接受到請求之後會把ticket送到認證系統進行效驗,檢查ticket的合法性。需要以下主要的功能:

a) 所有應用系統共享一個身份認證系統;
b) 所有應用系統能夠識別和提取ticket資訊;
c) 應用系統能夠辨識已登入的用戶,能自動判斷目前使用者是否登入過,從而完成單一登入的功能

#基於以上基本原則,本人用php語言設計了一套單一登入系統的程序,目前已投入正式生成伺服器運作。本系統程序,將ticket資訊以全系統唯一的 session id作為媒介,從而取得目前線上使用者的全站資訊(登陸狀態資訊及其他所需要處理的使用者全站資訊)。


二. 流程說明:


登陸流程:

#1. 第一次登陸某個站:

a)使用者輸入使用者名稱+密碼,向使用者驗證中心發送登入請求
b) 目前登入站點,透過webservice請求,使用者驗證中心驗證使用者名,密碼的合法性。如果驗證通過,則產生ticket,用於標識目前會話的用戶,並將目前登陸子站的網站識別碼記錄到用戶中心,最後
c) 將取得的用戶資料和ticket傳回給子站。如果驗證不通過,則傳回對應的錯誤狀態碼。

d) 根據上一步的webservice請求返回的結果,當前子站對用戶進行登陸處理:如狀態碼表示成功的話,則當前站點通過本站cookie保存ticket,並本站記錄用戶的登入狀態。狀態碼表示失敗的話,則給使用者對應的登入失敗提示。

2. 登陸狀態下,使用者前往另一子:

a) 透過本站cookie或session驗證使用者的登入狀態:如驗證通過,進入正常本站處理程序;否則戶中心驗證使用者的登入狀態(發送ticket到使用者驗證中心),如驗證通過,則對傳回的使用者資訊進行本機的登入處理,否則表示使用者未登入。


登出流程


a) 目前登出站清除使用者本站的登入狀態和本機儲存的使用者全站唯一的隨機id

b) 透過webservice接口,清除全站記錄的全站唯一的隨機id。 webservice介面會傳回,登出其他已登入子站的j​​avascript程式碼,本站輸出此程式碼。

c) js程式碼存取對應站W3C標準的登出腳本
三.程式碼說明:


本文所涉及到相關程式碼,已打包上傳,如有興趣,可在本文最後下載連結處點擊下載。

1. 登陸流程:

使用者從開啟瀏覽器開始,第一個登陸的子站點,必須呼叫UClientSSO::loginSSO()方法。此方法傳回全站唯一的隨機id用於識別該使用者。此隨機id在UClientSSO::loginSSO()中已透過本站cookie保存,即該子站點保留了使用者已登陸識別的存根於本站。
a) UClientSSO::loginSSO()方法如下:

#

<?php
/**
 * 用户验证中心 登陆用户处理
 *
 * @param string $username   - 用户名
 * @param string $password   - 用户原始密码
 * @param boolean $remember   - 是否永久记住登陆账号
 * @param boolean $alreadyEnc  - 传入的密码是否已经经过simpleEncPass加密过
 *
 * @return array  - integer $return[&#39;status&#39;] 大于 0:返回用户 ID,表示用户登录成功
 *                        -1:用户不存在,或者被删除
 *                        -2:密码错
 *                                                 -11:验证码错误
 *                     string $return[&#39;username&#39;]   : 用户名
 *                     string $return[&#39;password&#39;]   : 密码
 *                     string $return[&#39;email&#39;]    : Email
 */

static public function loginSSO($username, $password, $remember=false, $alreadyEnc=false) {
self::_init();
self::_removeLocalSid();
$ret = array();

//
//1. 处理传入webservice接口的参数
//
$_params = array(
        &#39;username&#39; => $username,
        &#39;password&#39; => $alreadyEnc ? trim($password) : self::simpleEncPass(trim($password)),
        &#39;ip&#39;    => self::onlineip(),
        &#39;siteFlag&#39; => self::$site,
        &#39;remember&#39; => $remember
);
$_params[&#39;checksum&#39;] = self::_getCheckSum($_params[&#39;username&#39;] . $_params[&#39;password&#39;] .
$_params[&#39;ip&#39;] . $_params[&#39;siteFlag&#39;] . $_params[&#39;remember&#39;]);

//
// 2.调用webservice接口,进行登陆处理
//
$aRet = self::_callSoap(&#39;loginUCenter&#39;, $_params);

if (intval($aRet[&#39;resultFlag&#39;]) > 0 && $aRet[&#39;sessID&#39;]) {
//成功登陆
//设置本地session id
self::_setLocalSid($aRet[&#39;sessID&#39;]);

//设置用户中心的统一session id脚本路径
self::$_synloginScript = urldecode($aRet[&#39;script&#39;]);

$ret = $aRet[&#39;userinfo&#39;];
} else {

$ret[&#39;status&#39;] = $aRet[&#39;resultFlag&#39;];
}

return $ret;
}//end of function         

//b) 用户验证中心的webservice服务程序,接收到登陆验证请求后,调用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[&#39;status&#39;];        //
$ret[&#39;resultFlag&#39;] = $res_login;

if ($res_login < 1) {
//登陆失败
} else {

//登陆成功
$_SESSION[self::$_ucSessKey] = $arr_login_res;

$_SESSION[self::$_ucSessKey][&#39;salt&#39;] =
self::_getUserPassSalt($_SESSION[self::$_ucSessKey][&#39;username&#39;], $_SESSION[self::$_ucSessKey][&#39;password&#39;]);

$ret[&#39;userinfo&#39;] = $_SESSION[self::$_ucSessKey];
$ret[&#39;sessID&#39;]  = session_id();    //生成全站的唯一session id,作为ticket全站通行

//
//合作中心站回调登陆接口(设置用户中心的统一session id)
//
self::_createCoSitesInfo();
$uinfo = array();
$_timestamp = time();
$_rawCode = array(
            &#39;action&#39; => &#39;setSid&#39;,
            &#39;sid&#39;  => $ret[&#39;sessID&#39;],
            &#39;time&#39;  => $_timestamp,
);
if ($remember) {
$uinfo = array(
                &#39;remember&#39; => 1,
                &#39;username&#39; => $username,
                &#39;password&#39; => $password
);
}

$ret[&#39;script&#39;] = &#39;&#39;;
$_rawStr = http_build_query(array_merge($_rawCode, $uinfo));

//
// 合作站点的全域cookie设置脚本地址
//
foreach ((array)self::$_coSitesInfo as $_siteInfo) {
$_code = self::authcode($_rawStr, &#39;ENCODE&#39;, $_siteInfo[&#39;key&#39;]);
$_src = $_siteInfo[&#39;url&#39;] . &#39;?code=&#39; . $_code . &#39;&time=&#39; . $_timestamp;
$ret[&#39;script&#39;] .= urlencode(&#39;&#39;);
}

//
// 记住已登陆战
//
self::registerLoggedSite($siteFlag, $ret[&#39;sessID&#39;]);

unset($ret[&#39;userinfo&#39;][&#39;salt&#39;]);
}

return $ret;
}

?>
登入後複製



2. 本站登陸成功後,進行在地化的使用者登陸處理,其後會驗證使用者是否登陸只在本地驗證。 (本地訪問登陸用戶狀態的信息,請設定為關閉瀏覽器就退出)

3. 當檢測用戶登陸狀態時,請先調用本地的驗證處理,若本地驗證不通過,再調用UClientSSO::checkUserLogin()方法到使用者中心偵測使用者的登陸狀態。

a) UClientSSO::checkUserLogin()方法如下:

<?php
 /**
 * 用户单点登陆验证函数
 *
 * @return array  - integer $return[&#39;status&#39;] 大于 0:返回用户 ID,表示用户登录成功
 *                                                  0:用户没有在全站登陆
 *                        -1:用户不存在,或者被删除
 *                        -2:密码错
 *                        -3:未进行过单点登陆处理
 *                                                 -11:验证码错误
 *                     string $return[&#39;username&#39;]   : 用户名
 *                     string $return[&#39;password&#39;]   : 密码
 *                     string $return[&#39;email&#39;]    : Email
 */
 public static function checkUserLogin(){
 self::_init();
 $ret = array();
 $_sessId = self::_getLocalSid();
 if (empty($_sessId)) {
 //永久记住账号处理
 if(isset($_COOKIE[_UC_USER_COOKIE_NAME]) && !empty($_COOKIE[_UC_USER_COOKIE_NAME])) {
 
 //
 // 根据cookie里的用户名和密码判断用户是否已经登陆。
 //
 $_userinfo = explode(&#39;|g|&#39;, self::authcode($_COOKIE[_UC_USER_COOKIE_NAME], &#39;DECODE&#39;, self::$_authcodeKey));
 
 $username = $_userinfo[0];
 $password = isset($_userinfo[1]) ? $_userinfo[1] : &#39;&#39;;
 if (empty($password)) {
 $ret[&#39;status&#39;] = -3;
 } else {
 return self::loginSSO($username, $password, true, true);
 }
 
 } else {
 $ret[&#39;status&#39;] = -3;
 }
 
 } else {
 //
 //本站原先已经登陆过,通过保留的sesson id存根去用户中心验证
 //
 $_params = array(
             &#39;sessId&#39;  => $_sessId,
             &#39;siteFlag&#39; => self::$site,
             &#39;checksum&#39; => md5($_sessId . self::$site . self::$_mcComunicationKey)
 );
 $aRet = self::_callSoap(&#39;getOnlineUser&#39;, $_params);
 if (intval($aRet[&#39;resultFlag&#39;]) > 0) {
 //成功登陆
 $ret = $aRet[&#39;userinfo&#39;];
 } else {
 $ret[&#39;status&#39;] = $aRet[&#39;resultFlag&#39;];
 }
 }
 
 return $ret;
 }       
 
 b) 用户验证中心的webservice服务程序,接收到检验登陆的请求后,调用UCenter::getOnlineUser()方法来处理登陆请求:
 [php]/**
 * 根据sid,获取当前登陆的用户信息
 *
 * @param string $sessId    - 全站唯一session id,用做ticket
 * @return array
 */
 /**
 * 根据sid,获取当前登陆的用户信息
 *
 * @param string $sessId    - 全站唯一session id,用做ticket
 * @return array
 */
 static public function getOnlineUser($sessId, $siteFlag) {
 self::_init();
 session_id(trim($sessId));
 session_start();
 
 $ret = array();
 $_userinfo = $_SESSION[self::$_ucSessKey];
 
 if (isset($_userinfo[&#39;username&#39;]) && isset($_userinfo[&#39;password&#39;]) &&
 self::_getUserPassSalt($_userinfo[&#39;username&#39;], $_userinfo[&#39;password&#39;])) {
 $ret[&#39;resultFlag&#39;] = "1";
 $ret[&#39;userinfo&#39;] = $_userinfo;
 
 self::registerLoggedSite($siteFlag, $sessId);        //记住已登陆战
 unset($ret[&#39;userinfo&#39;][&#39;salt&#39;]);
 } else {
 $ret[&#39;resultFlag&#39;] = "0";
 }
 
 return ($ret);
 }
 ?>
登入後複製



4. 單點登出時,呼叫UClientSSO::logoutSSO( )方法。呼叫成功後,如需其他已登陸站立即登出,請呼叫 UClientSSO::getSynloginScript()方法取得W3C標準的script,在頁面輸出。

a) UClientSSO::logoutSSO()方法如下:        

<?php
/**
* 全站单点登出
* - 通过webservice请求注销掉用户的全站唯一标识
*
* @return integer  1: 成功
*                   -11:验证码错误
*/
public static function logoutSSO(){
    self::_init();
    $_sessId = self::_getLocalSid();
    //
    //本站没有登陆的话,不让同步登出其他站
    //
    if (empty($_sessId)) {
        self::_initSess(true);
        return false;
    }
    $_params = array(
        &#39;sessId&#39;  => $_sessId,
        &#39;siteFlag&#39; => self::$site,
        &#39;checksum&#39; => md5($_sessId . self::$site . self::$_mcComunicationKey)
    );
    $aRet = self::_callSoap(&#39;logoutUCenter&#39;, $_params);
    if (intval($aRet[&#39;resultFlag&#39;]) > 0) {
        //成功登出
        self::_removeLocalSid();        //移除本站记录的sid存根
        self::$_synlogoutScript = urldecode($aRet[&#39;script&#39;]);
        $ret = 1;
    } else {
        $ret = $aRet[&#39;resultFlag&#39;];
    }
    return intval($ret);
}          [/php]
    b) 用户验证中心的webservice服务程序,接收到全站登出请求后,调用UCenter::loginUCenter()方法来处理登陆请求:
/**
* 登出全站处理
*
* @param string - 全站唯一session id,用做ticket
* @return boolean
*/
static public function logoutUCenter($sessId) {
    self::_init();
    session_id(trim($sessId));
    session_start();
    $_SESSION = array();
    return empty($_SESSION) ? true : false;
}
?>
登入後複製



四. 代碼部署:        

1. 使用者驗證中心設定               

a) 使用者驗證中心提供給分站提供的webservice服務介面文件,即UserSvc.php部署在hostname/webapps/port/ UserSvc.php中。查看wsdl內容,請造訪http://www.php.cn/ UserSvc.php?wsdl

b) 使用者中心使用者單點服務類別檔案為UCenterSSO.class.php,檔案路徑為在hostname/ webapps/include /UCenterSSO.class.php。該檔案為使用者單點登陸處理 的服務端類,被hostname/webapps/port/ UserSvc.php呼叫。用於取得使用者的登陸訊息,是否單點登陸的狀態訊息,單點登出處理等。

c) 使用者驗證中心透過W3C標準,利用cookie方式記錄,刪除全站統一的使用者唯一隨機id 的腳本檔案為hostname/webapps/port/cookie_mgr.php.

2 . 子站點設定               

a) 各子站點請將,UClientSSO.class.php部署在使用者中心服務用戶端目錄下。部署好後,請修改最後一行的UClientSSO::setSite('1'); 參數值為使用者驗證中心統一指派給各站的識別id.

b) 在部署的使用者中心服務用戶端套件下的api目錄下下,請將logout_sso.php腳本轉移到此處,並編寫進行本站登出的處理腳本。

c) 在子網站驗證使用者登陸狀態的程式碼部分,額外增加到使用者中心的單點登陸驗證的處理。

即在先通過本站驗證使用者的登陸狀態,如果未通過驗證,則去用戶中心驗證。驗證操作要呼叫UClientSSO::checkUserLogin();接口,接口意義請查看程式碼註解。

d) 在分站的登出處理腳本中,透過UClientSSO::getSynlogoutScript();取得script串輸出即可。

五.擴充功能:      

1. 記錄追蹤所有線上使用者

因為所有使用者的登入都要經過使用者驗證中心,所有用戶的ticket都在驗證中心生成,可以將使用者和該ticket(session id)在記憶體表中建立一個映射表。得到所有線上使用者的記錄表。

後期如有必要追蹤使用者狀態來實現其他功能,只要追蹤這個映射表就可以了。其他功能可為: 取得線上使用者列表,判斷使用者線上狀態,取得線上使用者人數等。

2. 特殊統計處理

因為整個系統登入登出要經過使用者驗證中心,所以可以針對使用者的特殊統計進行處理。如用戶每天的登入次數,登陸時間,登陸狀態失效時間,各時段的線上用戶人數走勢等。

六. 其他事項:      



1. 本站登陸狀態有效時間問題:                  
##全站要求使用使用者時位時請求使用者狀態在關閉瀏覽器時就失效。要求各分站對session或cookie的處理方式依照如下進行:

a) Session方式記錄使用者登陸狀態的網站

請在網站公用腳本開始處,新增一下程式碼


<?php
 session_write_close();
 ini_set(&#39;session.auto_start&#39;, 0);          //关闭session自动启动
 ini_set(&#39;session.cookie_lifetime&#39;, 0);      //设置session在浏览器关闭时失效
 ini_set(&#39;session.gc_maxlifetime&#39;, 3600); //session在浏览器未关闭时的持续存活时间   
 ?>
登入後複製


b) cookie方式記錄使用者登陸狀態的網站

請在設定使用者登陸狀態的cookie時,設定cookie有效時間為null.


 以上是PHP版單點登陸實現方案的實例 的內容,更多相關內容請關注PHP中文網(www.php.cn)!


本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1665
14
CakePHP 教程
1424
52
Laravel 教程
1322
25
PHP教程
1270
29
C# 教程
1249
24
PHP和Python:比較兩種流行的編程語言 PHP和Python:比較兩種流行的編程語言 Apr 14, 2025 am 12:13 AM

PHP和Python各有優勢,選擇依據項目需求。 1.PHP適合web開發,尤其快速開發和維護網站。 2.Python適用於數據科學、機器學習和人工智能,語法簡潔,適合初學者。

PHP行動:現實世界中的示例和應用程序 PHP行動:現實世界中的示例和應用程序 Apr 14, 2025 am 12:19 AM

PHP在電子商務、內容管理系統和API開發中廣泛應用。 1)電子商務:用於購物車功能和支付處理。 2)內容管理系統:用於動態內容生成和用戶管理。 3)API開發:用於RESTfulAPI開發和API安全性。通過性能優化和最佳實踐,PHP應用的效率和可維護性得以提升。

PHP:網絡開發的關鍵語言 PHP:網絡開發的關鍵語言 Apr 13, 2025 am 12:08 AM

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP與Python:了解差異 PHP與Python:了解差異 Apr 11, 2025 am 12:15 AM

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP的持久相關性:它還活著嗎? PHP的持久相關性:它還活著嗎? Apr 14, 2025 am 12:12 AM

PHP仍然具有活力,其在現代編程領域中依然佔據重要地位。 1)PHP的簡單易學和強大社區支持使其在Web開發中廣泛應用;2)其靈活性和穩定性使其在處理Web表單、數據庫操作和文件處理等方面表現出色;3)PHP不斷進化和優化,適用於初學者和經驗豐富的開發者。

PHP和Python:代碼示例和比較 PHP和Python:代碼示例和比較 Apr 15, 2025 am 12:07 AM

PHP和Python各有優劣,選擇取決於項目需求和個人偏好。 1.PHP適合快速開發和維護大型Web應用。 2.Python在數據科學和機器學習領域佔據主導地位。

PHP與其他語言:比較 PHP與其他語言:比較 Apr 13, 2025 am 12:19 AM

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

PHP和Python:解釋了不同的範例 PHP和Python:解釋了不同的範例 Apr 18, 2025 am 12:26 AM

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

See all articles