做web开发,必然会涉及到Session,这是由于http协议本身是无状态的(每次响应都是独立的,彼此间没有联系),所以如果需要在页面跳转间保持某个用户的身份,就要在每次连接时告诉服务器端你的唯一标示号,即Session ID。这样,服务器端便可通过Session ID得到所需的数据。
在PHP中,Session是通过$_SESSION这个全局变量来set/get的,不过在使用之前要先初始化。初始化是通过 session_start函数(如果php.ini中将session.auto_start设为1,则会自动初始化),之后PHP会为request 自动生成一个唯一随机数作为Session ID,生成算法默认提供了MD5 (128 bits) 和SHA-1 (160 bits),由php.ini中session.hash_function设定。其实也可以自定义,比如在随机数基础上将来访者的IP地址也加入到算法中,像CodeIgniter1.7.2中代码:
- $sessid = ”;
- while (strlen($sessid) < 32)
- {
- $sessid .= mt_rand(0, mt_getrandmax());
- }
- // To make the session ID even more secure we’ll combine it with the user’s IP
- $sessid .= $this->CI->input->ip_address();
- $sessid = md5(uniqid($sessid, TRUE))
$sessid = ''; while (strlen($sessid) < 32) { $sessid .= mt_rand(0, mt_getrandmax()); } // To make the session ID even more secure we'll combine it with the user's IP $sessid .= $this->CI->input->ip_address(); $sessid = md5(uniqid($sessid, TRUE))
生成的ID存放在服务器的某一目录下,这由php.ini中session.save_path配置。如果要在多个服务器中同步session id,你可以将其存放在数据库或共享缓存中。这需要你自定义一系列Session的读写方法,并在调用session_start函数前先设定好,以下面代码为例(来自php document中的一段示例代码):
- CREATE TABLE `ws_sessions` (
- `session_id` varchar(255) BINARY NOT NULL DEFAULT ”,
- `session_expires` int(10) UNSIGNED NOT NULL DEFAULT ‘0’,
- `session_data` text,
- PRIMARY KEY (`session_id`)
- ) TYPE=InnoDB;
CREATE TABLE `ws_sessions` ( `session_id` varchar(255) BINARY NOT NULL DEFAULT '', `session_expires` int(10) UNSIGNED NOT NULL DEFAULT '0', `session_data` text, PRIMARY KEY (`session_id`) ) TYPE=InnoDB;
- <?php
- class session {
- // session-lifetime
- var $lifeTime;
- // mysql-handle
- var $dbHandle;
- function open($savePath, $sessName) {
- // get session-lifetime
- $this->lifeTime = get_cfg_var(“session.gc_maxlifetime”);
- // open database-connection
- $dbHandle = @mysql_connect(“server”,”user”,”password”);
- $dbSel = @mysql_select_db(“database”,$dbHandle);
- // return success
- if(!$dbHandle || !$dbSel)
- return false;
- $this->dbHandle = $dbHandle;
- return true;
- }
- function close() {
- $this->gc(ini_get(‘session.gc_maxlifetime’));
- // close database-connection
- return @mysql_close($this->dbHandle);
- }
- function read($sessID) {
- // fetch session-data
- $res = mysql_query(“SELECT session_data AS d FROM ws_sessions
- WHERE session_id = ‘$sessID’
- AND session_expires > “.time(),$this->dbHandle);
- // return data or an empty string at failure
- if($row = mysql_fetch_assoc($res))
- return $row[‘d’];
- return “”;
- }
- function write($sessID,$sessData) {
- // new session-expire-time
- $newExp = time() + $this->lifeTime;
- // is a session with this id in the database?
- $res = mysql_query(“SELECT * FROM ws_sessions
- WHERE session_id = ‘$sessID'”,$this->dbHandle);
- // if yes,
- if(mysql_num_rows($res)) {
- // …update session-data
- mysql_query(“UPDATE ws_sessions
- SET session_expires = ‘$newExp’,
- session_data = ‘$sessData’
- WHERE session_id = ‘$sessID'”,$this->dbHandle);
- // if something happened, return true
- if(mysql_affected_rows($this->dbHandle))
- return true;
- }
- // if no session-data was found,
- else {
- // create a new row
- mysql_query(“INSERT INTO ws_sessions (
- session_id,
- session_expires,
- session_data)
- VALUES(
- ‘$sessID’,
- ‘$newExp’,
- ‘$sessData’)”,$this->dbHandle);
- // if row was created, return true
- if(mysql_affected_rows($this->dbHandle))
- return true;
- }
- // an unknown error occured
- return false;
- }
- function destroy($sessID) {
- // delete session-data
- mysql_query(“DELETE FROM ws_sessions WHERE session_id = ‘$sessID'”,$this->dbHandle);
- // if session was deleted, return true,
- if(mysql_affected_rows($this->dbHandle))
- return true;
- // …else return false
- return false;
- }
- function gc($sessMaxLifeTime) {
- // delete old sessions
- mysql_query(“DELETE FROM ws_sessions WHERE session_expires < “.time(),$this->dbHandle);
- // return affected rows
- return mysql_affected_rows($this->dbHandle);
- }
- }
- $session = new session();
- session_set_save_handler(array(&$session,”open”),
- array(&$session,”close”),
- array(&$session,”read”),
- array(&$session,”write”),
- array(&$session,”destroy”),
- array(&$session,”gc”));
- session_start();
- // etc…
- ?>
<?php class session { // session-lifetime var $lifeTime; // mysql-handle var $dbHandle; function open($savePath, $sessName) { // get session-lifetime $this->lifeTime = get_cfg_var("session.gc_maxlifetime"); // open database-connection $dbHandle = @mysql_connect("server","user","password"); $dbSel = @mysql_select_db("database",$dbHandle); // return success if(!$dbHandle || !$dbSel) return false; $this->dbHandle = $dbHandle; return true; } function close() { $this->gc(ini_get('session.gc_maxlifetime')); // close database-connection return @mysql_close($this->dbHandle); } function read($sessID) { // fetch session-data $res = mysql_query("SELECT session_data AS d FROM ws_sessions WHERE session_id = '$sessID' AND session_expires > ".time(),$this->dbHandle); // return data or an empty string at failure if($row = mysql_fetch_assoc($res)) return $row['d']; return ""; } function write($sessID,$sessData) { // new session-expire-time $newExp = time() + $this->lifeTime; // is a session with this id in the database? $res = mysql_query("SELECT * FROM ws_sessions WHERE session_id = '$sessID'",$this->dbHandle); // if yes, if(mysql_num_rows($res)) { // ...update session-data mysql_query("UPDATE ws_sessions SET session_expires = '$newExp', session_data = '$sessData' WHERE session_id = '$sessID'",$this->dbHandle); // if something happened, return true if(mysql_affected_rows($this->dbHandle)) return true; } // if no session-data was found, else { // create a new row mysql_query("INSERT INTO ws_sessions ( session_id, session_expires, session_data) VALUES( '$sessID', '$newExp', '$sessData')",$this->dbHandle); // if row was created, return true if(mysql_affected_rows($this->dbHandle)) return true; } // an unknown error occured return false; } function destroy($sessID) { // delete session-data mysql_query("DELETE FROM ws_sessions WHERE session_id = '$sessID'",$this->dbHandle); // if session was deleted, return true, if(mysql_affected_rows($this->dbHandle)) return true; // ...else return false return false; } function gc($sessMaxLifeTime) { // delete old sessions mysql_query("DELETE FROM ws_sessions WHERE session_expires < ".time(),$this->dbHandle); // return affected rows return mysql_affected_rows($this->dbHandle); } } $session = new session(); session_set_save_handler(array(&$session,"open"), array(&$session,"close"), array(&$session,"read"), array(&$session,"write"), array(&$session,"destroy"), array(&$session,"gc")); session_start(); // etc... ?>
除了上述方法外还有其他办法可以保持Session的同步,可以参考PHP SESSION解惑一文中第四部分“session的同步”。
下面再谈谈Session ID的传递方式:Cookie和URL传递。
Cookie是比较常用的方式,在这种模式下,启动Session后服务器会在HTTP Response中自动加上header(‘Set-Cookie: session_name()=session_id(); path=/’),并在以后的请求中加上这个Cookie。当从该页跳转到的新页面并调用session_start()后,PHP将检查与给定ID相关联的服务器端存贮的session数据,如果没找到,则新建一个数据集。但是有一点,这种传递方式必须在用户浏览器开启Cookie的情况下才可用,如果万一用户关闭了Cookie,那么只好选择另外一种通过URL参数传递Session ID。
开启URL传递需要在php.ini中设置session.use_trans_sid(文档中提示使用这种方式会有安全风险,因为它显示地将Session ID放在url中,所以除非迫不得已不要选择此方式),并在代码中做如下修改:
- // 如果客户端使用cookie,可直接传递session到page2.php
- echo ‘<br /><a href=”page2.php”>page 2</a>';
- // 如果客户端禁用cookie
- echo ‘<br /><a href=”page2.php?’ . SID . ‘”>page 2</a>';
- /*
- 默认php5.2.1下,SID只有在cookie被写入的同时才会有值,如果该session
- 对应的cookie已经存在,那么SID将为(未定义)空
- */
// 如果客户端使用cookie,可直接传递session到page2.php echo '<br /><a href="page2.php">page 2</a>'; // 如果客户端禁用cookie echo '<br /><a href="page2.php?' . SID . '">page 2</a>'; /* 默认php5.2.1下,SID只有在cookie被写入的同时才会有值,如果该session 对应的cookie已经存在,那么SID将为(未定义)空 */
关于Session的内容还有很多,具体可查看官方手册,如果有新的总结会继续更新。
最新评论