PHP中的Session机制

做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中代码:

Php代码 复制代码
  1. $sessid = ”;
  2. while (strlen($sessid) < 32)
  3. {
  4. $sessid .= mt_rand(0, mt_getrandmax());
  5. }
  6. // To make the session ID even more secure we’ll combine it with the user’s IP
  7. $sessid .= $this->CI->input->ip_address();
  8. $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中的一段示例代码):

Sql代码 复制代码
  1. CREATE TABLE `ws_sessions` (
  2. `session_id` varchar(255) BINARY NOT NULL DEFAULT ”,
  3. `session_expires` int(10) UNSIGNED NOT NULL DEFAULT ‘0’,
  4. `session_data` text,
  5. PRIMARY KEY  (`session_id`)
  6. ) 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代码 复制代码
  1. <?php
  2. class session {
  3. // session-lifetime
  4. var $lifeTime;
  5. // mysql-handle
  6. var $dbHandle;
  7. function open($savePath, $sessName) {
  8. // get session-lifetime
  9. $this->lifeTime = get_cfg_var(“session.gc_maxlifetime”);
  10. // open database-connection
  11. $dbHandle = @mysql_connect(“server”,”user”,”password”);
  12. $dbSel = @mysql_select_db(“database”,$dbHandle);
  13. // return success
  14. if(!$dbHandle || !$dbSel)
  15. return false;
  16. $this->dbHandle = $dbHandle;
  17. return true;
  18. }
  19. function close() {
  20. $this->gc(ini_get(‘session.gc_maxlifetime’));
  21. // close database-connection
  22. return @mysql_close($this->dbHandle);
  23. }
  24. function read($sessID) {
  25. // fetch session-data
  26. $res = mysql_query(“SELECT session_data AS d FROM ws_sessions
  27. WHERE session_id = ‘$sessID’
  28. AND session_expires > “.time(),$this->dbHandle);
  29. // return data or an empty string at failure
  30. if($row = mysql_fetch_assoc($res))
  31. return $row[‘d’];
  32. return “”;
  33. }
  34. function write($sessID,$sessData) {
  35. // new session-expire-time
  36. $newExp = time() + $this->lifeTime;
  37. // is a session with this id in the database?
  38. $res = mysql_query(“SELECT * FROM ws_sessions
  39. WHERE session_id = ‘$sessID'”,$this->dbHandle);
  40. // if yes,
  41. if(mysql_num_rows($res)) {
  42. // …update session-data
  43. mysql_query(“UPDATE ws_sessions
  44. SET session_expires = ‘$newExp’,
  45. session_data = ‘$sessData’
  46. WHERE session_id = ‘$sessID'”,$this->dbHandle);
  47. // if something happened, return true
  48. if(mysql_affected_rows($this->dbHandle))
  49. return true;
  50. }
  51. // if no session-data was found,
  52. else {
  53. // create a new row
  54. mysql_query(“INSERT INTO ws_sessions (
  55. session_id,
  56. session_expires,
  57. session_data)
  58. VALUES(
  59. ‘$sessID’,
  60. ‘$newExp’,
  61. ‘$sessData’)”,$this->dbHandle);
  62. // if row was created, return true
  63. if(mysql_affected_rows($this->dbHandle))
  64. return true;
  65. }
  66. // an unknown error occured
  67. return false;
  68. }
  69. function destroy($sessID) {
  70. // delete session-data
  71. mysql_query(“DELETE FROM ws_sessions WHERE session_id = ‘$sessID'”,$this->dbHandle);
  72. // if session was deleted, return true,
  73. if(mysql_affected_rows($this->dbHandle))
  74. return true;
  75. // …else return false
  76. return false;
  77. }
  78. function gc($sessMaxLifeTime) {
  79. // delete old sessions
  80. mysql_query(“DELETE FROM ws_sessions WHERE session_expires < “.time(),$this->dbHandle);
  81. // return affected rows
  82. return mysql_affected_rows($this->dbHandle);
  83. }
  84. }
  85. $session = new session();
  86. session_set_save_handler(array(&$session,”open”),
  87. array(&$session,”close”),
  88. array(&$session,”read”),
  89. array(&$session,”write”),
  90. array(&$session,”destroy”),
  91. array(&$session,”gc”));
  92. session_start();
  93. // etc…
  94. ?>
<?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中,所以除非迫不得已不要选择此方式),并在代码中做如下修改:

Php代码 复制代码
  1. // 如果客户端使用cookie,可直接传递session到page2.php
  2. echo ‘<br /><a href=”page2.php”>page 2</a>';
  3. // 如果客户端禁用cookie
  4. echo ‘<br /><a href=”page2.php?’ . SID . ‘”>page 2</a>';
  5. /*
  6. 默认php5.2.1下,SID只有在cookie被写入的同时才会有值,如果该session
  7. 对应的cookie已经存在,那么SID将为(未定义)空
  8. */
// 如果客户端使用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的内容还有很多,具体可查看官方手册,如果有新的总结会继续更新。

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Protected by WP Anti Spam