zoukankan      html  css  js  c++  java
  • Php解决跨域名共享session方案整理专题

    Php解决跨域名共享session方案整理专题 2009-12-22 17:01:44
    
    分类:
    
    Php解决跨域名共享session方案整理专题
    
    [整理人:hkebao#126.com 整理时间:2009-12-22]
    
    测试环境:
    
    文件目录:F:PHPopensourceaistockbot_0.2-04_beta-2aistockbot  
    
    IP: 192.168.100.90
    
    域名:shop.hn81.com
    
     
    
    文件目录:E:uchomewordpress
    
    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或其他的设备中去!)
    
     
  • 相关阅读:
    Linux 工具箱—17款文件管理器
    RMAN 初学者指南
    Oracle监听器Server端与Client端配置实例
    mysqlproxy完成mysql读写分离
    linux端口映射
    MYSQL分布式集群使用主从复制
    SQL高级运用
    Redis安装与使用
    mysql cpu 负载率超过理想值,解决方案总结
    揭秘:HR是如何做背景调查的?你真的就可以隐瞒事实了吗?
  • 原文地址:https://www.cnblogs.com/afei-happy/p/3513572.html
Copyright © 2011-2022 走看看