PHP学习笔记之多服务器共享Session

为什么需要多服务器共享Session?
假如一个公司需要三台服务器来实现公司业务.一台网页服务器(www.xxx.com), 一台邮件服务器(mail.xxx.com), 一台图片服务器(pic.xxx.com).
它们共享一套用户系统,同时需要实现单点登陆.就是一个用户在其中一台服务器登陆后, 当跳转到其他服务器就不需要再次登陆了. 共享用户系统
只需要三台服务器都同时访问一个数据库表来认证即可. 但当用户跳转时候,事实是要求用户重新登陆.没有实现单点登陆的需求. 那么这时候就需要
多服务器共享Session.

实现过程:
一是用户资料共享
二是共享客户端的 SESSION ID
三是共享服务器端的 SESSION 数据


如何实现用户资料共享呢?
建立一套独立的用户认证系统, 当检查用户在其中一台服务器检测没有登陆,则跳转到用户认证系统. 通过验证后再跳回原来需要登陆的服务器.

如何共享客户端的 SESSION ID?
通过设置Cookie的作用域的值为同一个域名.代码如下:

ini_set
("session.cookie_domain"".xxx.com");



如何共享服务器端的 SESSION 数据
通过不同的保存方式来实现, 我之前的写"PHP学习笔记之Session保存方式篇"提到了不同的保存方式.以数据库保存为例说明:
代码如下:
上面三种方式都执行一个统一的接口. session接口. 保存此接口为interface_session.php.
以下代码都经过测试, 都能正常使用. 可以直接在项目中使用

实现代码:
<?php
// Session处理接口, 此接口由其他类执行
interface session 
{   
    
/**
     * Construct
     * 
     * Functions:
     * 1.initalize handler
     * 2.initalize Session and start session
     */
    
public function __construct();
    
    
/**
     * Initalize handler
     */
    
public function initHandler();
    
    
/**
     *  initalize Session and start session
     */
    
public function initSession();
    
    
/**
     * Work as construct.
     *
     * @param string $sesion_path
     * @param string $session_name
     */
    
public function open($sesion_path$session_name);
    
    
/**
     * Work as destruct
     */
    
public function close();
    
    
/**
     * Read Session information
     *
     * @param string $session_id
     */
    
public function read($session_id);
    
    
/**
     * Write data
     *
     * @param unknown_type $session_id
     * @param unknown_type $session_data
     */
    
public function write($session_id$session_data);
    
    
/**
     * Destroy sessoin
     *
     * @param sessoin $session_id
     */
    
public function destroy($session_id);
    
    
/**
     * Garbage collector
     *
     * @param unknown_type $maxlifetime
     */
    
public function gc($maxlifetime);
    
    
/**
     * Destruct
     */
    
public function __destruct();
}
?>


<?php
require_once dirname(__FILE__) . '/interface_session.php' ;

/**
 * Handle Session information by file handler
 * 
 * You need a table to store session information 
 * Look like this:
 * 
 * CREATE TABLE `session` (
 *   `session_id`      varchar(32)          NOT NULL ,
 *   `session_data`    text                 NOT NULL  default '' ,
 *   `session_updated` int(10)     unsigned NOT NULL  default '0',
 *   PRIMARY KEY (`session_id`),
 *   KEY (`session_updated`)
 * ) TYPE=MyISAM;
 * 
 */
class DatabaseSession implements session 
{
    
    
// Const
    
const USE_TRANS_ID ;
    const 
MAXLIFETIME  3600;
    const 
USE_COOKIE   ;
    const 
COOKIE_PATH  '/';
    const 
COOKIE_DOMAIN '.';
    const 
SESSION_SAVE_PATH '/';
    const 
SESSION_NAME 'databasesession';
    const 
SESSION_PREFIX 'ds_';
    const 
DATABASE_USER 'root';
    const 
DATABASE_PASS '';
    const 
DATABASE_DSN 'mysql:host=localhost;dbname=interview';
    
    
    public static 
$session_id  ;
    public static 
$session_save_path ;
    public static 
$handler ;
    
    
/**
     * Construct
     * 
     * Functions:
     * 1.initalize handler
     * 2.initalize Session and start session
     */
    
public function __construct(){
        
self::initHandler();
        
self::initSession();
    }
    
    
/**
     * Initalize handler
     */
    
public function initHandler(){
        
$session_id $_COOKIE[self::SESSION_NAME];
        
        if ( !
$session_id ) {
            
self::$session_id md5(microtime());
            
setcookie(self::SESSION_NAMEself::$session_id);
        }
        
        if ( !
class_exists("PDO") ) {
            die(
"You must install PDO extension first!");
        }
        
        
self::$handler = new PDO(DatabaseSession::DATABASE_DSNDatabaseSession::DATABASE_USER DatabaseSession::DATABASE_PASS) ;
    }
    
    
/**
     *  initalize Session and start session
     */
    
public function initSession(){
        
// ini setting
        
ini_set('session.use_trans_id'self::USE_TRANS_ID);
        
ini_set('session.maxlifetime'self::MAXLIFETIME);
        
ini_set('session.use_cookie'self::USE_COOKIE);
        
ini_set('session.cookie_path'self::COOKIE_PATH);
        
ini_set('session.cookie_domain'self::COOKIE_DOMAIN);
        
ini_set('session.save_path'self::SESSION_SAVE_PATH);
        
ini_set('session.name'self::SESSION_NAME);
        
        
// use user level handler not files
        
session_module_name("user");
        
        
// setting handler
        
session_set_save_handler(
            array(
'DatabaseSession''open'),
            array(
'DatabaseSession''close'),
            array(
'DatabaseSession''read'),
            array(
'DatabaseSession''write'),
            array(
'DatabaseSession''destroy'),
            array(
'DatabaseSession''gc')
        );
        
        
// start session
        
session_start();

        return 
true;
    }
    
    
/**
     * Work as construct.
     *
     * @param string $sesion_path
     * @param string $session_name
     */
    
public function open($session_save_path$session_name){
        
DatabaseSession::$session_save_path $session_save_path;
        return 
true;
    }
    
    
/**
     * Work as destruct
     */
    
public function close(){
        return 
true;
    }
    
    
/**
     * Read Session information
     *
     * @param string $session_id
     */
    
public function read($session_id){

        if ( 
$session_id ) {
            
$sql "SELECT * FROM `session` WHERE `session_id`= '{$session_id}'" ;
            
$pre DatabaseSession::$handler->prepare($sql);
            
$pre->execute();
            if ( 
$re $pre->fetch() ) {
                return 
$re['session_data'] ;
            }
        }
        
        return 
true;
    }
    
    
/**
     * Write data
     *
     * @param string $session_id
     * @param string $session_data
     */
    
public function write($session_id$session_data){
        
        
$sql "SELECT * FROM `session` WHERE `session_id`= '{$session_id}' " ;
        
$pre DatabaseSession::$handler->prepare($sql);
        
$pre->execute();
        
$updated time();
        if ( 
$re $pre->fetch() ) {
            
$sql "UPDATE `session` set `session_data` = '{$session_data}', `session_updated`='{$updated}'  WHERE `session_id`= '{$session_id}' " ;
        } else {
            
$sql "INSERT INTO `session` (`session_id`, `session_data`, `session_updated`) VALUES('{$session_id}', '{$session_data}', '{$updated}')" ;
        }
        
$pre DatabaseSession::$handler->prepare($sql);
        
$pre->execute();           
        return 
true;
    }
    
    
/**
     * Destroy sessoin
     *
     * @param sessoin $session_id
     */
    
public function destroy($session_id){
        if ( 
$session_id ) {
            
$sql "DELETE FROM `session` WHERE `session_id`='{$session_id}'";
            
$pre DatabaseSession::$handler->prepare($sql);
            
$pre->execute();
        }
        return 
true;
    }
    
    
/**
     * Garbage collector
     *
     * @param int $maxlifetime
     */
    
public function gc($maxlifetime){
        if ( 
$maxlifetime ) {
            
$updated time() + $maxlifetime ;
            
$sql "DELETE FROM `session` WHERE `session_updated` < '{$updated}'";
            
$pre DatabaseSesson::$handler->prepare($sql);
            
$pre->execute();
        }
        return 
true;
    }
    
    
/**
     * Destruct
     */
    
public function __destruct(){
        return 
true;
    }
}
$fs = new DatabaseSession();
?>


使用:
按你的实际需要修改数据库服务器地址和端口, 在需要Session的文件最开始处, 直接包含此database_session.php文件.
这样三台服务器都可以保存Session到统一的数据库上.当用户在其中一台服务器登陆后, 在其他两台服务器则不需要再重新登陆
现在用上面的代码来实际地验证上面的理论. 以下例子在本地设置虚拟主机的方式实现. 转移到各个独立的服务器理论上是通行

以上面的公司业务作例:
web服务器:http://web.nc.com
邮件服务器:http://mail.nc.com
图片服务器:http://picture.nc.com
认证服务器:http://passport.nc.com

测试软件包下载:
sso.rar


1. 用户共享
通过user类来实现用户认证.此类包含在sso.rar

2.Session ID 共享和Session信息共享
通过Session接口和DatabaseSession类来实现.此类包含在sso.rar

3.实现方式

 1.安装wamp

2.解压sso.rar软件包,放在www/目录下.结构如下
 /sso
/web
/mail
/picture
/passport


3.设置C:\windows\system32\drivers\etc\hosts.增加

127.0.0.1  http
://web.nc.com
   
127.0.0.1  http://mail.nc.com
   
127.0.0.1  http://picture.nc.com
   
127.0.0.1  http://passort.nc.com


4.设置虚拟主机C:\wamp\bin\apache\apache2.2.6\conf\extra\httpd-vhosts.conf.增加
 

  
<VirtualHost *:80>
    
ServerAdmin ncnynl@gmail.com
    DocumentRoot 
"c:/wamp/www/sso/web"
    
ServerName web.nc.com
</VirtualHost

<
VirtualHost *:80>
    
ServerAdmin ncnynl@gmail.com
    DocumentRoot 
"c:/wamp/www/sso/mail"
    
ServerName mail.nc.com
</VirtualHost

<
VirtualHost *:80>
    
ServerAdmin ncnynl@gmail.com
    DocumentRoot 
"c:/wamp/www/sso/picture"
    
ServerName picture.nc.com
</VirtualHost

<
VirtualHost *:80>
    
ServerAdmin ncnynl@gmail.com
    DocumentRoot 
"c:/wamp/www/sso/passort"
    
ServerName passort.nc.com
</VirtualHost>

5.建立数据库interview

CREATE DATABASE interview
;


6.建立用户表

  CREATE TABLE 
`user` (
  `
uidint(10unsigned NOT NULL auto_increment,
  `
unamevarchar(100) default NULL,
  `
passvarchar(32) default NULL,
  
PRIMARY KEY  (`uid`),
  
KEY `unamepass` (`uname`,`pass`)
  ENGINE=MyISAM

  7.Session表

CREATE TABLE 
`session` (
 `
session_id`      varchar(32)          NOT NULL ,
 `
session_data`    text                 NOT NULL  default '' ,
 `
session_updatedint(10)     unsigned NOT NULL  default '0',
 
PRIMARY KEY (`session_id`),
  
KEY (`session_updated`)
TYPE=MyISAM;  

8.设置user.php中Passport类,$dsn, $user, $pass
9.设置DatabaseSession类,DATABASE_DSN,DATABASE_USER, DATABASE_PASS
10.当你访问http://web.nc.com, 会跳转到http://passport.nc.com, 第一次需要增加用户,输入用户和密码.选择增加用户.
成功登陆后会自动跳转到http://web.nc.com. 则在访问其他http://picture.nc.com, http://mail.nc.com均显示已经登陆
  在其中一个站点登出,则其他站点同时登出.



参考:

http://hi.baidu.com/sing520/blog/item ... a312efdea2c8c3fd78c4.html