• php单点登陆简单实现 (iframe方式)


    有四个网站分别为:
    www.a.com
    www.b.com
    www.c.com
    www.sso.com
    
    需求是如果我们在sso登陆后,其他网站也会显示登陆中,不需要重复登陆,退出时,其他网站也会失效。
     
    解决流程如下:
    1、我们需要统一这四个站的session存储方式。 (session共享,保存到mysql中)
    2、登陆和退出的请求统一由sso这个站处理。(其他站全部跳转到sso上)
    3、当用户在sso.com登陆时,我们在验证完用户名和密码正确后,通过获取session_id和生成一段skey加密串保存到user表的相应字段中。然后跳转到一个中间页面,访页面通过iframe的方式引入a.com,b.com,和c.com。
    <iframe src="http://www.a.com/sso.php?sessid=xxx&skey=xxx" width="0" height="0" frameborder="0">
    </iframe>
    <iframe src="http://www.b.com/sso.php?sessid=xxx&skey=xxx" width="0" height="0" frameborder="0">
    </iframe>
    <iframe src="http://www.c.com/sso.php?sessid=xxx&skey=xxx" width="0" height="0" frameborder="0">
    </iframe>
    
    (*传给a,b,c这三个站的sessid是经过加密的,为了安全。)
     
    4、a,b,c这三个站的sso.php拿到sessid和skey解密后得到session_id,然后到user表中进行比对,如果正确,设置当前会话的session_id,然后session_start(),因为四个站的session是共享的,所以a,b ,c三个站就拿到了在sso上登陆时存储的会话数据。
    //sessid是sso.com上产生的session_id
    session_id(sessid);
    session_start();
    
    具体的代码如下:
    www.sso.com/index.php
    <?php
    header('Content-Type:text/html;charset=utf-8');
    require_once './DBSession.php';
    session_start();
    
    if(array_key_exists('userInfo', $_SESSION) && !empty($_SESSION['userInfo'])) {
        $userInfo = $_SESSION['userInfo'];
        echo '欢迎', $userInfo['name'], '<br />';
        echo '<a href="./logout.php">退出</a>';
    } else {
        header('Location: ./login.html');
    }
    
    www.sso.com/login.html
    <!DOCTYPE HTML>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
        * {padding:0;margin:0;}
        .loginForm {450px;height:auto;margin:100px auto 0 auto;}
        .loginForm table {100%;border-collapse:collapse;}
        .loginForm table td, .loginForm table th {border:1px solid #dcdcdc;padding:5px;}
        </style>
    </head>
    <body>
        <form action="./login.php" method="post" class="loginForm">
            <table>
                <tr>
                    <th colspan="2">用户登陆</th>
                </tr>
                <tr>
                    <td>用户名:</td>
                    <td><input type="text" name="uname" value="" /></td>
                </tr>
                <tr>
                    <td>密码:</td>
                    <td><input type="password" name="upwd" value="" /></td>
                </tr>
                <tr>
                    <td> </td>
                    <td><input type="submit" name="submit" value="登陆" /></td>
                </tr>
            </table>
        </form>
    </body>
    </html>
    
    www.sso.com/login.php
    <?php
    require_once './DBSession.php';
    require_once './DB.php';
    session_start();
    
    if(!empty($_POST)) {
        $uname = !empty($_POST['uname']) ? trim($_POST['uname']) : '';
        $upwd = !empty($_POST['upwd']) ? trim($_POST['upwd']) : '';
    
        $upwd = md5($upwd);
        $ret = mysql_query("select `id`,`name` from `user` where `name`='{$uname}' and `pwd`='{$upwd}';", $db);
        $data = mysql_fetch_assoc($ret);
        if($data) {
            $userInfo = array(
                'id' => $data['id'],
                'name' => $data['name'],
            );
            $_SESSION['userInfo'] = $userInfo;
            //通过用户ID,用户名和时间戳的md5生成skey
            $skey = md5($data['id'] . $data['name'] .  time());
            $sessId = session_id();
    
            $userId = $data['id'];
            $ret = mysql_query("update `user` set `sessid`='$sessId', `skey`='$skey' where `id`={$userId};", $db);
            if(mysql_affected_rows($db)) {
    
                //取skey前三个字符和后三个字符生成key
                $key = substr($skey, 0, 3) . substr($skey, -1, 3);
                $lifeTime = ini_get('session.gc_maxlifetime');
                $sessId = authcode($sessId, 'ENCODE', $key, $lifeTime);
    
                $appSSOUrl = array(
                    'http://www.a.com/sso.php',
                    'http://www.b.com/sso.php',
                    'http://www.c.com/sso.php',
                );
                
                require 'iframe.php';
            }
        } else {
            header('Location: ./login.html');
        }
    }
    
    www.sso.com/iframe.php
    <!DOCTYPE HTML>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <?php foreach($appSSOUrl as $item) { ?>
        <iframe src="<?php echo $item . '?sessid=' . $sessId . '&skey=' . $skey;?>" width="0" height="0" frameborder="0"></iframe>
        <?php } ?>
    </body>
    <script type="text/javascript">
        window.onload = function() {
             location.href = 'index.php';
        };
    </script>
    </html>
    
    www.sso.com/logout.php
    <?php
    require_once './DBSession.php';
    require_once './DB.php';
    
    session_start();
    
    if(array_key_exists('userInfo', $_SESSION) && !empty($_SESSION['userInfo'])) {
        $uid = $_SESSION['userInfo']['id'];
    
        $ret = mysql_query("update `user` set `sessid`='', `skey`='' where id={$uid}", $db);
        if(session_destroy()) {
            header('Location: ./login.html');
        }
    }
    
    www.a.com/index.php
    <?php
    header('Content-Type:text/html;charset=utf-8');
    require_once './DBSession.php';
    session_start();
    
    if(array_key_exists('userInfo', $_SESSION) && !empty($_SESSION['userInfo'])) {
        $userInfo = $_SESSION['userInfo'];
        echo '欢迎', $userInfo['name'], '<br />';
        echo '<a href="http://www.sso.com/logout.php">退出</a>';
    } else {
        header('Location: http://www.sso.com/login.html');
    }
    
    www.a.com/sso.php
    <?php
    require_once './DBSession.php';
    require_once './DB.php';
    
    $sessid = !empty($_GET['sessid']) ? trim($_GET['sessid']) : '';
    $skey = !empty($_GET['skey']) ? trim($_GET['skey']) : '';
    
    if(empty($sessid) || empty($skey)) {
        exit;
    }
    
    $key = substr($skey, 0, 3) . substr($skey, -1, 3);
    $sessid = authcode($sessid, 'DECODE', $key);
    if(!empty($sessid)) {
        $ret = mysql_query("select count(*) as num from user where sessid='{$sessid}' and skey='{$skey}'");
        $row = mysql_fetch_assoc($ret);
        if($row['num']) {
            session_id($sessid);
            session_start();
        }
    } else {
        header('Location: http://www.sso.com/login.html');
    }
    
    b和c站的文件跟a站是一样的,只需复制a站即可,这里就不重复了。
     
    DBSession.php代码:
    <?php
    class DBSession {
        protected $db = null;
        protected $lifeTime = 0;
        protected $sessTable = '';
    
        public function __construct($db, $sessTable) {
            $this->db = $db;
            $this->sessTable = $sessTable;
            $this->lifeTime = ini_get('session.gc_maxlifetime');
    
            ini_set('session.save_handler', 'user');
            session_set_save_handler(
                array($this, "open"),
                array($this, "close"),
                array($this, "read"),
                array($this, "write"),
                array($this, "destroy"),
                array($this, "gc")
            );
            register_shutdown_function('session_write_close');
        }
    
        public function open($savePath, $sessName) {
            return true;
        }
    
        public function close() {
            $this->gc($this->lifeTime); 
            return true;
        }
    
        public function read($sessId) {
            $time = time();
            $ret = mysql_query("SELECT `data` FROM `{$this->sessTable}` WHERE `sid`='{$sessId}' AND `expire` > {$time};", $this->db);
            if($ret) {
               $row = mysql_fetch_assoc($ret);
               return $row['data']; 
            }
            return ''; 
        }
    
        public function write($sessId, $sessData) {
            $expire = time() + $this->lifeTime;
            $sessData = mysql_real_escape_string($sessData);
            
            $ret = mysql_query("SELECT COUNT(*) AS cnt FROM `{$this->sessTable}` WHERE `sid`='{$sessId}';", $this->db);
            $row = mysql_fetch_assoc($ret);
            if($row['cnt']) {
                $sql = "UPDATE `{$this->sessTable}` SET `data`='{$sessData}', `expire`={$expire} WHERE `sid`='{$sessId}';";
            } else {
                $sql = "INSERT INTO `{$this->sessTable}` (`sid`,`expire`,`data`) VALUES('{$sessId}',{$expire},'{$sessData}');";
            }
            mysql_query($sql, $this->db);
            if(mysql_affected_rows($this->db)) {
                return true;
            }
            return false;
        }
    
        public function destroy($sessId) {
            mysql_query("DELETE FROM `{$this->sessTable}` WHERE `sid`='{$sessId}';", $this->db); 
            if(mysql_affected_rows($this->db)) {
                return true;
            }
            return false;
        }
    
        public function gc($lifeTime) {
            $time = time();
            mysql_query("DELETE FROM `{$this->sessTable}` WHERE `expire` < {$time};", $this->db); 
            return mysql_affected_rows($this->db);
        }
    }
    
    $sessDb = mysql_connect('127.0.0.1', 'root', '') or die('connect error');
    mysql_select_db('test', $sessDb) or ('select db error');
    mysql_query('set names utf8', $sessDb);
    new DBSession($sessDb, 'session');
    
    session表结构如下:
    CREATE TABLE `session` (
    `sid` varchar(32) NOT NULL DEFAULT '' COMMENT 'session_id',
    `expire` int(11) NOT NULL COMMENT '过期时间',
    `data` text COMMENT 'session数据',
    PRIMARY KEY (`sid`)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='session表';
    
    DB.php文件代码如下:
    <?php
    $db = mysql_connect('127.0.0.1', 'root', '') or die('connect error');
    mysql_select_db('test', $db) or ('select db error');
    mysql_query('set names utf8', $db);
    
    user表结构如下:
    CREATE TABLE `user` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
      `name` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名',
      `pwd` varchar(32) NOT NULL DEFAULT '' COMMENT '用户密码(md5)',
      `sessid` varchar(32) NOT NULL DEFAULT '' COMMENT 'session_id(未加密)',
      `skey` varchar(32) NOT NULL DEFAULT '' COMMENT '安全码',
      PRIMARY KEY (`id`)
    ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户表';
    
    加密解密函数authcode(取自Discuz)代码如下:
    function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0){
        if($operation == 'DECODE') {
            $string = str_replace('[a]','+',$string);
            $string = str_replace('[b]','&',$string);
            $string = str_replace('[c]','/',$string);
        }
        $ckey_length = 4;
        $key = md5($key ? $key : 'livcmsencryption');
        $keya = md5(substr($key, 0, 16));
        $keyb = md5(substr($key, 16, 16));
        $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
        $cryptkey = $keya.md5($keya.$keyc);
        $key_length = strlen($cryptkey);
        $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
        $string_length = strlen($string);
        $result = '';
        $box = range(0, 255);
        $rndkey = array();
        for($i = 0; $i <= 255; $i++) {
            $rndkey[$i] = ord($cryptkey[$i % $key_length]);
        }
        for($j = $i = 0; $i < 256; $i++) {
            $j = ($j + $box[$i] + $rndkey[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }
        for($a = $j = $i = 0; $i < $string_length; $i++) {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }
        if($operation == 'DECODE') {
            if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
                return substr($result, 26);
            } else {
                return '';
            }
        } else {
            $ustr = $keyc.str_replace('=', '', base64_encode($result));
            $ustr = str_replace('+','[a]',$ustr);
            $ustr = str_replace('&','[b]',$ustr);
            $ustr = str_replace('/','[c]',$ustr);
            return $ustr;
        }
    }
    
    我们通过在sso.com上登陆
    php单点登陆简单实现 (iframe方式) - 怀素真 - 因上努力 果上随缘
    登成功后会跳转到index.php
    php单点登陆简单实现 (iframe方式) - 怀素真 - 因上努力 果上随缘
     然后我们访问a.com,b.com ,c.com查看
    php单点登陆简单实现 (iframe方式) - 怀素真 - 因上努力 果上随缘
    我们点击任一网站退出,其他网站都已退出。
     
    原理其实很简单,我们统一了四个站的session存储方式,然后让a,b,c三个站拿到用户在sso上登陆的会话id。这样会话信息就共享了。不过实现的很粗糙,仅供学习使用。
     
    参考资料:

    http://www.zhihu.com/question/19779937

  • 相关阅读:
    用Python实现QQ找茬游戏外挂工具
    Python常用模块
    将Qt 动态链接生成的exe及依赖dll打包方法
    Qt之VLFeat SLIC超像素分割(Cpp版)
    android studio下的NDK开发详解(一)
    条件注释判断浏览器版本<!--[if lt IE 9]>
    人脸识别中的八大难题,何时能解
    人脸识别简史与近期进展
    openCV之头文件分析
    看(学习)代码流程
  • 原文地址:https://www.cnblogs.com/jkko123/p/6294579.html
Copyright © 2020-2023  润新知