原系统是在PHPCMS基础上进行开发的,应用部署为www.domain.com。中途开发基于CI框架的财务功能,部署为finance.domain.com。为了实现Session共用,需同步两套框架Session体系,即:同样的Session存储介质,同样的Session_id,同样的Session.cookie_domain,功能同样的open ,read ,write ,close ,destory ,gc方法。
限定:本项目Session基于Database驱动。
目标:修改CI系统,与PHPCMS兼容。
步骤:
1、修改config.php文件(位于application/config/config.php)
$config['sess_driver'] = 'database';//修改为数据库驱动
$config['sess_cookie_name'] = 'PHPSESSID';//与PHPCMS的存储Session_id的Cookiename一致
$config['sess_save_path'] = 'XX_session';//与PHPCMS用于存储session的表名一致
2、修改Session_database_driver.php(位于system/libraries/Session/drivers/Session_database_driver.php)
IC的db驱动中比PHPCMS多了锁的机制。CI采用数据库字符锁,MySQL下是SELECT GET_LOCK('str',300) / SELECT RELEASE_LOCK
。
不过我们不用理会,这里主要修改read,write,destory,gc四个方法的CRUD逻辑,并与PHPCMS中的字段一致。
public function read($session_id)
{
if ($this->_get_lock($session_id) !== FALSE)
{
// Prevent previous QB calls from messing with our queries
$this->_db->reset_query();
// Needed by write() to detect session_regenerate_id() calls
$this->_session_id = $session_id;
$this->_db
->select('data')
->from($this->_config['save_path'])
->where('sessionid', $session_id);
if ($this->_config['match_ip'])
{
$this->_db->where('ip', $_SERVER['REMOTE_ADDR']);
}
if (($result = $this->_db->get()->row()) === NULL)
{
// PHP7 will reuse the same SessionHandler object after
// ID regeneration, so we need to explicitly set this to
// FALSE instead of relying on the default ...
$this->_row_exists = FALSE;
$this->_fingerprint = md5('');
return '';
}
// PostgreSQL's variant of a BLOB datatype is Bytea, which is a
// PITA to work with, so we use base64-encoded data in a TEXT
// field instead.
$result = ($this->_platform === 'postgre')
? base64_decode(rtrim($result->data))
: $result->data;
$this->_fingerprint = md5($result);
$this->_row_exists = TRUE;
return $result;
}
$this->_fingerprint = md5('');
return '';
}
public function write($session_id, $session_data)
{
// Prevent previous QB calls from messing with our queries
$this->_db->reset_query();
// Was the ID regenerated?
if ($session_id !== $this->_session_id)
{
if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id))
{
return $this->_failure;
}
$this->_row_exists = FALSE;
$this->_session_id = $session_id;
}
elseif ($this->_lock === FALSE)
{
return $this->_failure;
}
if ($this->_row_exists === FALSE)
{
$insert_data = array(
'sessionid' => $session_id,
'ip' => $_SERVER['REMOTE_ADDR'],
'lastvisit' => time(),
'userid' => isset($_SESSION['userid']) ? $_SESSION['userid'] : 0,
//'_userid' => isset($_SESSION['_userid']) ? $_SESSION['_userid'] : 0,
'roleid' => isset($_SESSION['roleid']) ? $_SESSION['roleid'] : 0,
'groupid' => isset($_SESSION['groupid']) ? $_SESSION['groupid'] : 0,
'm' => '',//应该url帮助函数提取片段即目录
'c' => __CLASS__ ,
'a' => __FUNCTION__ ,
'data' => ($this->_platform === 'postgre' ? base64_encode($session_data) : $session_data)
);
if ($this->_db->insert($this->_config['save_path'], $insert_data))
{
$this->_fingerprint = md5($session_data);
$this->_row_exists = TRUE;
return $this->_success;
}
return $this->_failure;
}
$this->_db->where('sessionid', $session_id);
if ($this->_config['match_ip'])
{
$this->_db->where('ip', $_SERVER['REMOTE_ADDR']);
}
$update_data = array('lastvisit' => time());
if ($this->_fingerprint !== md5($session_data))
{
$update_data['data'] = ($this->_platform === 'postgre')
? base64_encode($session_data)
: $session_data;
}
if ($this->_db->update($this->_config['save_path'], $update_data))
{
$this->_fingerprint = md5($session_data);
return $this->_success;
}
return $this->_failure;
}
/**
* Destroy
*
* Destroys the current session.
*
* @param string $session_id Session ID
* @return bool
*/
public function destroy($session_id)
{
if ($this->_lock)
{
// Prevent previous QB calls from messing with our queries
$this->_db->reset_query();
$this->_db->where('sessionid', $session_id);
if ($this->_config['match_ip'])
{
$this->_db->where('ip', $_SERVER['REMOTE_ADDR']);
}
if ( ! $this->_db->delete($this->_config['save_path']))
{
return $this->_failure;
}
}
if ($this->close() === $this->_success)
{
$this->_cookie_destroy();
return $this->_success;
}
return $this->_failure;
}
// ------------------------------------------------------------------------
/**
* Garbage Collector
*
* Deletes expired sessions
*
* @param int $maxlifetime Maximum lifetime of sessions
* @return bool
*/
public function gc($maxlifetime)
{
// Prevent previous QB calls from messing with our queries
$this->_db->reset_query();
return ($this->_db->delete($this->_config['save_path'], 'timestamp < '.(time() - $maxlifetime)))
? $this->_success
: $this->_failure;
}
3、数据库sessionid字段长度从32改成50。phpcms设置的sessionid字段长度是32,造成CI生成的sessionid长度如果超过32就会被截掉一些。
做完这一切,看起来似乎完工了,试着运行一下
在CI的控制器中插入:
$this->session->set_userdata(array(
'username' => 'tangwm',
'userid' => '1jcsxdl',
));
在PHPCMS系统中输出:
echo $_SESSION['username'];
然并卵,啥也没输出。
4、问题在哪里?
打开debug工具对请求的CI控制器进行监控,发现存储的sessionid的cookie作用域为finance.domain.com,跟踪代码发现
而主站PHPCMS框架中的获取域是www.domain.com
这时需要统一两边用来存储sessionid的cookie作用域,并保证这个cookie作用域设置不会影响其它cookie作用域
CI修改两个地方:
1)、配置文件config.php(位于application/config/config.php)
中加入两个用来存储sessionid的cookies配置(ci原来的是用的统一的cookie设置,这里我做成Session专用的)
$config['sess_cookie_domain'] = '.domain.com';
$config['sess_cookie_path'] = '';
2)、session.php文件
修改protected function _configure(&$params)函数中的这一段:
isset($params['sess_cookie_path']) OR $params['sess_cookie_path'] = config_item('sess_cookie_path');
isset($params['sess_cookie_domain']) OR $params['sess_cookie_domain'] = config_item('sess_cookie_domain');
isset($params['cookie_secure']) OR $params['cookie_secure'] = (bool) config_item('cookie_secure');
session_set_cookie_params(
$params['cookie_lifetime'],
$params['sess_cookie_path'],
$params['sess_cookie_domain'],
$params['cookie_secure'],
TRUE // HttpOnly; Yes, this is intentional and not configurable for security reasons
);
修改构造函数__construct这一段:
setcookie(
$this->_config['cookie_name'],
session_id(),
(empty($this->_config['cookie_lifetime']) ? 0 : time() + $this->_config['cookie_lifetime']),
$this->_config['sess_cookie_path'],
$this->_config['sess_cookie_domain'],
$this->_config['cookie_secure'],
TRUE
);
PHPCMS修改:
在配置文件(caches/configs/system.php)中加上
'session_cookie_domain' => 'nc.com', //session Cookie 作用域
在base.php中加入
ini_set("session.cookie_domain",pc_base::load_config('system','session_cookie_domain'));
对了,这句话加在哪一行?抛硬币决定吧
只要在define(‘IN_PHPCMS’, true);和类定义class pc_base 之间就可以
最后PHPCMS成功输出tangwm。反之在PHPCMS中写入session,CI也能成功读取。