Php解决跨域名共享session方案整理专题 2009-12-22 17:01:44 分类: Php解决跨域名共享session方案整理专题 [整理人:hkebao#126.com 整理时间:2009-12-22] 测试环境: 文件目录:F:\PHP\opensource\aistockbot_0.2-04_beta-2\aistockbot IP: 192.168.100.90 域名:shop.hn81.com 文件目录:E:\uchome\wordpress IP:192.168.100.40 域名:user.hn81.com 测试步骤: 1、 在shop域名下面创建一个文件里面记录用户的一个session值。PHP代码如下: <?php $lifeTime = 2 * 3600; session_set_cookie_params($lifeTime); //这个函数必须要在session_start之前调用 session_start(); $_SESSION["admin"] = "test"; ?> 说明:session_start() 函数 的作用就是创建session初始化一个变量。 请求地址:http://shop.hn81.com/test/create.php 能够创建一个session值! 2、 在同一个域名内获取这个session参数值! 请求地址:http://shop.hn81.com/test/read.php 能够获取到当前保存的session变量值! PS:这两个是在同一个域名下面的所以能够正常地进行读取 3、 在另一个域中请求这个地址:http://user.hn81.com/test/read.php 结果读不出来指定的session变量值。 以上是测试过程中发现的问题,现在讨论一下如何解决这个问题? 尝试一、通过带sessionid 的方法 PS: 1、通过PHP里面的session_id() ; 函数能够获取到当前的sessionid 值! 2、SESSION ID 可以通过两种方式保留在客户端,使得请求不同的页面时,PHP 程序可以获知客户端的 SESSION ID;一种是将 SESSION ID 自动加入到 GET 的 URL 中,或者 POST 的表单中,默认情况下,变量名为 PHPSESSID;另一种是通过 COOKIE,将 SESSION ID 保存在 COOKIE 中,默认情况下,这个 COOKIE 的名字为 PHPSESSID。 3、那么 SESSION 的数据保存在哪里呢?当然是在服务器端,但不是保存在内存中,而是保存在文件或数据库中。默认情况下,php.ini 中设置的 SESSION 保存方式是 files(session.save_handler = files),即使用读写文件的方式保存 SESSION 数据,而 SESSION 文件保存的目录由 session.save_path 指定,文件名以 sess_ 为前缀,后跟 SESSION ID,如:sess_c72665af28a8b14c0fe11afe3b59b51b。文件中的数据即是序列化之后的 SESSION 数据了。如果访问量大,可能产生的 SESSION 文件会比较多,这时可以设置分级目录进行 SESSION 文件的保存,效率会提高很多,设置方法为:session.save_path="N;/save_path",N 为分级的级数,save_path 为开始目录。当写入 SESSION 数据的时候,PHP 会获取到客户端的 SESSION_ID,然后根据这个 SESSION ID 到指定的 SESSION 文件保存目录中找到相应的 SESSION 文件,不存在则创建之,最后将数据序列化之后写入文件。读取 SESSION 数据是也是类似的操作流程,对读出来的数据需要进行解序列化,生成相应的 SESSION 变量。(session工作原理) 测试步骤: 1、 在创建session的服务器域下面。获取到当前的sessionid 然后创建超链接直接跳转到另一个域中去 <?php session_start(); if(isset($_SESSION["admin"])){ $sessionid = session_id() ; }else{ echo("e"); } ?> <a href="http://user.hn81.com/test/read.php?sessionid=<?php echo $sessionid;?>">链接</a> 带上sessionid 值。 2、 在另一个域中接收到这个sessionid 并提取出来这个值 if(isset($_GET["sessionid"])) { $sessionid = $_GET["sessionid"]; //获取到这个sessionid值! session_id($sessionid); //使用session session_start(); echo $_SESSION['admin']; //通过session 查找到实体数据出来! }else{ echo $_SESSION['admin']; //直接获取不能得到数据! echo "no"; } 心得:通过传递sessionid 能够解决跨域共享session数据! 尝试二、通过扩大session的作用域范围 PS:刚犯了一个错误。浏览器之间是不能共cookie的。因为像FF与IE保存cookie的目录都不一样怎么可能共享?搞糊涂了!!! 因为session要使用前提是客户端的COOKIE必须要开启。所以可以考虑扩大其作用域范畴! 这种解决办法其实算是对第一种解决办法的迂回吧!具体如下: PS:以后可以用这种办法! 1、 创建session的代码如下: <?php $lifeTime = 2 * 3600; session_start(); session_set_cookie_params($lifeTime); setcookie(session_name(), session_id(), time() + $lifeTime, "/","hn81.com"); //将session_id保存到客户端,呆会还会介绍将sessionid 保存到服务器端的解决办法! $_SESSION["admin"] = "test"; ?> 2、 同一个域的获取代码: <?php session_start(); if(isset($_SESSION["admin"])){ echo $_SESSION["admin"]; //直接获取 }else{ echo("e"); } ?> 3、 另一个域中获取SESSION数据 <?php $sessionid = $_COOKIE["PHPSESSID"]; //拿到COOKIE值即得到sessionid 值! session_id($sessionid); //通过ID去查找实体的SESSION数据文件 session_start(); echo $_SESSION['admin']; //获取数据 ?> PS:第二种办法是能够解决同一个大域共享session数据的办法的。即将key(ID)放到客户端 通过应用这种办法解决目前遇到的问题^_^。 这种情况:真实的session数据还是保存在服务器上面的。如果是多台服务器做负载均衡的话单用这种办法解决不了,为什么呢?因为每一台服务器上面保存的真实session数据不一致。所以可以将session层抽出来作持久化处理。所以就有了第三种办法! 尝试三、将session进行持久化。将session数据保存到数据库 具体实现的过程图: 将session的数据持久化处理。 这种情况也能够同一个应用跑在多台SERVER上面。 一般的代理转发架构如下: PS: 1、 ini_set 函数相当于是直接修改了php.ini 配置文件。 2、 php.ini 里面关于 session 的配置 [Session] session.save_handler = files ; handler used to store/retrieve data ; 存储/读入数据用的句柄 session.save_path = C:/php4 ; argument passed to save_handler ; in the case of files, this is the ; path where data files are stored ; 用于存放句柄文件的路径(一定要设置正确,否则不能执行) session.use_cookies = 1 ; whether to use cookies ; 是否使用 cookies session.name = PHPSESSID ; name of the session ; 在 cookie 里 session 使用的名字 ; is used as cookie name session.auto_start = 0 ; initialize session on request startup ; 是否在请求开始时自动启动,初始化 session session.cookie_lifetime = 0 ; lifetime in seconds of cookie ; or if 0, until browser is restarted ; 在 cookie 里面 session 存在的时间秒数,0 直到浏览器重新启动 session.cookie_path = / ; the path the cookie is valid for ; cookie 里存放数值的位置 session.cookie_domain = ; the domain the cookie is valid for ; 存放数值的 cookie的主机(主键) session.serialize_handler = php ; handler used to serialize data ; php is the standard serializer of PHP ; 序列化数据的句柄,标准句柄是 php session.gc_probability = 1 ; percentual probability that the ; 'garbage collection' process is started ; on every session initialization ; 打开每个session初始化时自动开始 垃圾收集进程 session.gc_maxlifetime = 1440 ; after this number of seconds, stored ; data will be seen as 'garbage' and ; cleaned up by the gc process ; 当超过这个时间,存储的的数据会被认为是垃圾,被 gc 进程清除 session.referer_check = ; check HTTP Referer to invalidate ; externally stored URLs containing ids ; 检查包含 ids 的 HTTP 里无效的外部保存 URLs的内容 session.entropy_length = 0 ; how many bytes to read from the file ; 从文件里读入的允许字节数 session.entropy_file = ; specified here to create the session id ; 指定在这里建立 session id ; session.entropy_length = 16 ; session.entropy_file = /dev/urandom session.cache_limiter = nocache ; set to {nocache,private,public} to ; determine HTTP caching aspects ; 确定 HTTP 缓存外貌 {nocache,private,public} session.cache_expire = 180 ; document expires after n minutes ; 超过 n 分钟文档到期 3、 session_set_save_handler 函数的作用是更改PHP默认的session方式。比如说现在要将session数据保存到数据库就可以用它! 测试过程如下:(以后可以直接复制运行我全部都通过测试的!) 一、 创建表 SQL语句如下: CREATE TABLE `mysession` ( `session_key` char(32) NOT NULL default '', `session_expiry` bigint(20) NOT NULL default '0', `session_data` longtext NOT NULL, PRIMARY KEY (`session_key`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 二、 编写自定义session处理代码具体代码如下: <?php function mysession_open($save_path, $session_name) { @mysql_connect("localhost", "root", "321") or die("数据库服务器连接失败"); @mysql_select_db("phpsess") or die("数据库不存在或不可用"); return true; } function mysession_close() { return true; } function mysession_read($key) { $expiry_time = time(); //获取Session失效时间 $ssql = "select session_data from mysession "." where session_key = '$key' and session_expiry > $expiry_time"; $query = @mysql_query($ssql) or die("SQL语句执行失败"); if($row = mysql_fetch_array($query)) return $row['session_data']; else return false; } function mysession_write($key, $data) { $expiry_time = time() + 500; //获取Session失效时间 $query = @mysql_query("select session_data from mysession "."where session_key = '$key'") or die("SQL语句执行失败"); //如果不存在,则执行插入操作,否则执行更新操作 if(mysql_numrows($query) == 0) { //执行SQL语句插入Session的值 $query = @mysql_query("insert into mysession(session_key,session_data,session_expiry) values('$key', '$data', $expiry_time)") or die("SQL语句执行失败"); } else { //执行SQL语句更新Session的值 $query = @mysql_query("update mysession set " ."session_data = '$data', session_expiry = $expiry_time " ."where session_key = '$key'") or die("SQL语句执行失败"); } return $query; } function mysession_destroy($key) { $query = @mysql_query("delete from mysession where session_key = '$key'") or die("SQL语句执行失败"); return $query; } function mysession_gc($expiry_time) { $expiry_time = time(); //执行SQL语句删除Session $query = @mysql_query("delete from mysession where session_expiry < $expiry_time") or die("SQL语句执行失败"); return $query; } //重新配置一下php的配置信息 ini_set('session.use_trans_sid', 0); //设置垃圾回收最大生存时间 ini_set('session.gc_maxlifetime', 50); //使用 COOKIE 保存 SESSION ID 的方式 ini_set('session.use_cookies',1); ini_set('session.cookie_path', '/'); //多主机共享保存 SESSION ID 的 COOKIE //这个非常关键。一定要将域定义好要不然会出问题。其他的域像.tianya.cn .sina.cn .126.com ini_set('session.cookie_domain',".hn81.com"); //将域定义好 //设置用户自定义Session存储 ini_set('session.save_handler', 'user'); session_set_save_handler('mysession_open', 'mysession_close', 'mysession_read', 'mysession_write', 'mysession_destroy', 'mysession_gc'); ?> 保存文件名为:session_inc.php 第三步、编写创建session的代码。如下 <?php include('session_inc.php'); $lifeTime = 2 * 3600; session_set_cookie_params($lifeTime); session_start(); $_SESSION['admin'] = "welcome to php"; //将session参数值定义好 setcookie(session_name(), session_id(), time() + $lifeTime, "/","hn81.com"); //这个的作用是将 sessionid暂保存到用户客户端中去。是为了能够让用户在下次访问的时候将ID带过来作为key 进行DB查询用 到此,我们的session已经成功创建了。现在我需要访问它! 第四步、同一个域名访问方式如下 注意:是同一个域名哦。这种情况其实就是就是在前端加了一个负载均衡。像haxproxy、nginx等实体的session数据保存到了单个服务器上。所以将其迁到数据库中去! 这种情况取session的办法如下: include('session_inc.php'); //包含session_set_save_handler定义的文件 session_start(); echo $_SESSION['admin']; //与传统的一样是吧。只是多了一个 include文件! 第五步、不同域名访问方式如下: 这个时候就要依靠客户端的cookie了。通过sessionid 值提取对应的SESSION数据 具体的代码如下: <?php include('session_inc.php'); //包含session_set_save_handler定义的文件 $sessionid = $_COOKIE["PHPSESSID"]; //拿COOKIE session_id($sessionid); //通过ID去查找实体的SESSION数据文件 session_start(); echo $_SESSION['admin']; //获取数据 相比来讲多了两步操作。 同时我查看了一下DB。有记录! 心得:通过这种办法将SESSION数据迁到数据库中去。能够让多台服务器、多个域名共享SESSION数据 其中(如果只是处理单台多域名的话我们是可以通过走COOKIE的方法解决见上面二。如果有关联到多台服务器的时候就要考虑将SESSION迁到DB或其他的设备中去!)