ホームページ  >  記事  >  バックエンド開発  >  PHPシングルサインオン導入ソリューション例

PHPシングルサインオン導入ソリューション例

黄舟
黄舟オリジナル
2017-02-28 09:18:591347ブラウズ

要約:

この記事では主に、Web サービス、セッション、Cookie テクノロジを使用したユニバーサル シングル サインオン システムの分析と設計について紹介します。具体的な実装言語は PHP です。シングル サインオンは、英語ではシングル サインオン (略して SSO) とも呼ばれ、現在の企業やネットワーク ビジネスにおけるユーザーの包括的な処理の重要な部分です。 SSO の定義は、複数のアプリケーション システムにおいて、ユーザーは一度ログインするだけで、相互に信頼されているすべてのアプリケーション システムにアクセスできることです。

動機:

ucenter のフルサイト ログイン方法を使用したことがある友人は、これが典型的なオブザーバー パターン ソリューションであることを知っているはずです。ユーザーセンターがサブジェクトとなり、そのオブザーバーの登録と削除はucenterのバックエンドで一元化されます。各サブアプリケーション サイトはオブザーバーに対応します。ユーザー センターでのすべてのログイン アクションは、js スクリプトをトリガーして、w3c 標準サブサイト ログイン インターフェイス (api/uc.php) をコールバックします。

この方法の欠点は主に次の 2 点だと思います: 1. サブサイトが多すぎると、それに応じてコールバック インターフェイスが増加します。これは、分散サブサイトの数によって制限されます。ログイン効率が低くなりすぎないようにするには? 2. 特定のサブステーションのコールバック インターフェイスに問題がある場合、デフォルトのログイン プロセスが停止します(ログイン プログラムの実行時間が制限される可能性があります)。ただし、対応する変電所の背後にある変電所のコールバック インターフェイスは調整できません

上記の問題に基づいて、実際の開発プロセス中に、別のシングル サインオン システムを設計しました

技術的な実装シングル サインオンの仕組み: ユーザーがアプリケーション システムに初めてアクセスするとき 1. この時点では、まだログインしていないため、提供されたログイン情報に基づいてログインするための認証システムが表示されます。ユーザーによって、認証システムは本人確認を実行し、検証に合格した場合、ユーザーに認証資格情報チケットを返します。他のアプリケーションを使用する場合、このチケットは独自の資格情報として使用されます。認証リクエストを受信した後、アプリケーション システムは検証のためにチケットを認証システムに送信します。チケットの有効性が検証に合格した場合、ユーザーは再度ログインすることなくアプリケーション システム 2 とアプリケーション システム 3 にアクセスできます。
SSO を実装するには、次の主な機能が必要であることがわかります:

a) すべてのアプリケーション システムは ID 認証システムを共有します。

b) すべてのアプリケーション システムはチケット情報を識別して抽出できます。 ) アプリケーションシステムは、ログインしているユーザーを識別し、現在のユーザーがログインしているかどうかを自動的に判断することができ、それによってシングルサインイン機能を完了します

上記の基本原則に基づいて、私はPHP言語を使用しますシングルサインのセット-on システム プロシージャが設計され、サーバーを正式に生成するために運用されています。このシステム プログラムは、システム全体の固有のセッション ID を持つチケット情報を媒体として使用して、現在のオンライン ユーザーのサイト全体の情報 (ログイン ステータス情報や処理が必要なユーザー全体のサイト情報) を取得します。


2. プロセスの説明:


ログインプロセス:
1. 初めてサイトにログインします:
a) ユーザーはユーザー名とパスワードを入力し、ユーザー認証センターにログイン要求を送信します。
b) 現在のログイン サイトでは、Web サービス リクエストを通じて、ユーザー検証センターがユーザー名とパスワードの正当性を検証します。検証に合格すると、現在のセッションのユーザーを識別するためのチケットが生成され、現在ログインしているサブサイトのサイト識別子がユーザー センターに記録されます。最後に、

c) は取得したユーザー データを返します。サブサイトへのチケット。検証が失敗した場合は、対応するエラー ステータス コードが返されます。

d) 前のステップの Web サービス リクエストによって返された結果に従って、現在のサブサイトはユーザーにログインします。ステータス コードが成功を示す場合、現在のサイトはこのサイトの Cookie を介してチケットを保存し、記録します。ユーザーのログインステータス。ステータス コードが失敗を示している場合、ユーザーには対応するログイン失敗プロンプトが表示されます。

2. ログインすると、ユーザーは別のページに移動します:

a) サイトの Cookie またはセッションを通じてユーザーのログイン ステータスを確認します。検証に合格した場合は、通常のサイト処理プログラムに入ります。そうでない場合は、ユーザー センターが検証します。ユーザーのログイン状態 (チケットをユーザー認証センターに送信します)。認証に合格した場合は、返されたユーザー情報に対してローカル ログイン処理が実行されます。そうでない場合は、ユーザーがログインしていないことを意味します。


ログアウトプロセス


a) 現在のログアウトにより、サイトのユーザーのログインステータスと、ローカルに保存されたサイト全体のユーザーの一意のランダム ID がクリアされます

b) Web サービスインターフェイスを通じて、サイト全体の一意のランダム ID がクリアされますサイト全体のランダムIDによって記録されたサイト。 Web サービス インターフェイスが戻り、ログインしている他のサブサイトの JavaScript コードをログアウトし、このサイトはこのコードを出力します。 c) js コードは、対応するサイトの W3C 標準ログアウト スクリプトにアクセスします


3. コードの説明:


この記事に関連するコードはパッケージ化され、アップロードされています。興味がある場合は、クリックしてダウンロードできます。この記事の最後のダウンロード リンクにあります。

1. ログインプロセス: ブラウザを開いて、最初にログインしたサブサイトは UClientSSO::loginSSO() メソッドを呼び出す必要があります。このメソッドは、サイト全体に一意でユーザーの識別に使用されるランダム ID を返します。このランダム ID は、UClientSSO::loginSSO() 内のこの Web サイトの Cookie を通じて保存されています。つまり、サブサイトは、この Web サイトでのユーザーのログイン ID のスタブを保持します。

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 標準スクリプトを取得し、ページに出力してください。

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;
}
?>



4. コードのデプロイメント:

1. ユーザー認証によって提供される Web サービス サービス インターフェイスファイル UserSvc.php は、hostname/webapps/port/UserSvc.php にデプロイされます。 wsdl コンテンツを表示するには、http://www.php.cn/UserSvc.php?wsdl

にアクセスしてください。 b) ユーザー センターのユーザー シングルポイント サービス クラス ファイルは UCenterSSO.class.php で、ファイル パスはホスト名にあります。 /webapps/include / UCenterSSO.class.php.このファイルはユーザーのシングル サインイン処理用のサーバー クラスであり、hostname/webapps/port/UserSvc.php によって呼び出されます。ユーザーのログイン情報、シングルポイントログインの有無に関するステータス情報、シングルログアウト処理などを取得するために使用されます。

c) ユーザー検証センターは W3C 標準に合格し、Cookie を使用してサイト全体の一意のランダム ID を削除します。

2 サブサイト設定の場合。各サブサイトでは、ユーザー センター サービス クライアント ディレクトリに UClientSSO.class.php を展開してください。デプロイ後、UClientSSO::setSite('1'); の最後の行を変更してください。パラメータ値は、ユーザー認証センターによって各サイトに一律に割り当てられた識別 ID です。

b) デプロイされたユーザー センター サービス クライアント パッケージの APIディレクトリにある場合は、logout_sso.php スクリプトをここに移動し、このサイトからログアウトするスクリプトを作成してください。

c) サブサイトでのユーザーのログイン状態を検証するためのコード セクションに、ユーザー センターでの追加のシングルポイント ログイン検証処理が追加されました。

つまり、まずこのサイトを通じてユーザーのログイン状態を確認し、認証に失敗した場合は、ユーザーセンターにアクセスして確認します。検証操作では、UClientSSO::checkUserLogin() インターフェイスを呼び出す必要があります。インターフェイスの意味については、コードのコメントを参照してください。

d) ブランチ ステーションのログアウト処理スクリプトで、UClientSSO::getSynlogoutScript(); を使用してスクリプト文字列出力を取得します。


5. 拡張機能:


1. すべてのオンライン ユーザーを記録および追跡します すべてのユーザー ログインはユーザー検証センターを経由する必要があるため、すべてのユーザー チケットは検証センターで生成され、ユーザーとチケット( session) は id にすることができます) はメモリ テーブルにマッピング テーブルを作成します。すべてのオンライン ユーザーの記録リストを取得します。

後で他の機能を実装するためにユーザーのステータスを追跡する必要がある場合は、このマッピングテーブルを追跡するだけです。その他の機能には、オンライン ユーザーのリストの取得、ユーザーのオンライン ステータスの決定、オンライン ユーザーの数の取得などがあります。

2. 特殊な統計処理

システム全体のログインとログアウトはユーザー認証センターを経由する必要があるため、ユーザーの特殊な統計処理が可能です。 1日あたりのユーザーログイン数、ログイン時間、ログイン状態の有効期限、各期間のオンラインユーザー数の推移など。


6. その他の事項:



1. 当サイトのログインステータスの有効期限:
ブラウザを閉じると、サイト全体でユーザーのログインステータスが無効になることが必要です。各ブランチ サイトは、次のようにセッションまたは Cookie を処理する必要があります:

a) セッション モードでユーザーのログイン ステータスを記録するサイト

サイトのパブリック スクリプトの先頭にコードを追加してください


<?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

というメソッドでユーザーのログイン状況を記録しているサイト



上記は、PHP版シングルサインの例の内容です。 -in 実装ソリューションの詳細については、PHP 中国語 Web サイト (www.php.cn) に注目してください。


-->

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。