为什么需要多服务器共享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 = 0 ;
const MAXLIFETIME = 3600;
const USE_COOKIE = 1 ;
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_NAME, self::$session_id);
}
if ( !class_exists("PDO") ) {
die("You must install PDO extension first!");
}
self::$handler = new PDO(DatabaseSession::DATABASE_DSN, DatabaseSession::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
/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` (
`uid` int(10) unsigned NOT NULL auto_increment,
`uname` varchar(100) default NULL,
`pass` varchar(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_updated` int(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

