这是我在CSDN发布的第一篇技术帖子,虽然很久就注册了CSDN,都是偶然看看而已,好了废话不多说,进入正题。
最近写PHP框架的时候遇到一个问题,就是用session保存数据的时候,经常遇到同时出现两个session_id,导致登录前和登录后两个文件session保存的数据不同。
网上找遍了技术文档,也看到有人遇到过类似的问题,但是没有解决出来,下面我就深入讲解下cookie和session的机制。
基础-简单介绍
session指的是一次会话的开始到结束,信息存储到服务端的/tmp文件夹下面,每次会话的开始都要执行一遍start_session(),当然也可以设置自动开启(默认关闭此选项)。
cookie是将信息存储到浏览器上面,默认浏览器关闭,则断开连接。
session和cookie的关系
session是通过cookie实现每个页面同步传输数据的,具体流程是:
开启session,然后生成session_id,session_id存储到cookie里面,默认存储变量是PHPSESSID,用户再同一个网站的时候,发送数据并携带cookie里的这个session_id,服务器识别session_id并抽取服务器里存储的数据,返回给客户端。PHP的session存储是基于浏览器的cookie实现的,当一个会话结束后,即浏览器关闭,那么cookie将失效,服务器的session也将失效。设置cookie的过期时间即是session的过期时间。
上面就是基本的session和cookie的工作流程,我们经常需要设置一个过期时间,如下:
function start_session($expire = 0) {
if ($expire == 0) {
$expire = ini_get('session.gc_maxlifetime');//获取系统设置的过期时间
} else {
ini_set('session.gc_maxlifetime', $expire);//临时设置此次会话的过期时间
}
$session_cache_dir = APP_PATH.'cache/tmp';//设置服务器session缓存存储路径
if(!file_exists($session_cache_dir)){
mkdir($session_cache_dir,0777,true);
}
ini_set('session.save_path',$session_cache_dir);//设置服务器session缓存存储路径
ini_set("session.cookie_httponly", 1);//设置cookie在浏览器不可访问,感觉没什么效果
session_set_cookie_params($expire);//设置cookie过期时间
session_start();//启动session
setcookie(session_name(), session_id(), time() + $expire);//设置PHPSESSID存储的时间,即设置session的过期时间
}
上面的代码我都加了注释,我在前台页面设置了不断更新cookie过期时间的ajax代码,如:
//jq代码
var interval = setInterval(function(){
$.ajax({
url:"{fun U('Index/update_session_maxlifetime')}", //请求的url地址
dataType:"json", //返回格式为json
async:true,//请求是否异步,默认为异步,这也是ajax重要特性
data:{}, //参数值
type:"GET", //请求方式
beforeSend:function(){
//请求前的处理
},
success:function(r){
//请求成功时处理
console.log(r);
},
complete:function(){
//请求完成的处理
},
error:function(){
//请求出错处理
}
})
},5000);
})
PHP代码如下:
//更新session的过期时间
function update_session_maxlifetime(){
//
$cache_time = (int)webConf('cache_time');
$cache_time = $cache_time==0 ? 600 : $cache_time;
//session_name()读取当前的cookie里存储session_id的参数,即PHPSESSID。session_id()读取对应的值,即session_id值
setcookie(session_name(), session_id(), time() + $cache_time);
JsonReturn(session_id().'-'.$_COOKIE[session_name()].'更新session成功!');
}
前端返回打印刷新session成功,就是让系统每隔几秒访问一下这个方法,更新cookie里的session_id的存储时间,使之保持持续不断会话。
设想是美好的,上面一套下来会导致2个或多个cookie。当前页面刷新确实没问题,保持着session,页面也不会提示重新登录,但是你再访问其他链接的时候,就会出现提示错误,打印session_id发现与当前页面不一样,F12查看cookie能看到PHPSESSID有两个不同的数值。(缓存时间设置在60s左右,比较短时间才能发现,如果长时间就不太容易发现这个问题)
那么究竟是哪里出了问题呢?
每次登录也都是开启了session,设置缓存时间,用户登录授权,session存储个人信息。有人想到是session回收机制?回收机制是到时间了,服务器将过期的session收回,但是我明明更新了session时间啊。。。在这条路绕了好多天。。。
终于,在昨晚我想通了。
其实是因为cookie在不同页面有可能存在不同的值,每次进入页面的时候,session_id()获取的是当前页面的cookie里的PHPSESSID,如果这个页面在过期时间内没有访问,那么当前的cookie就不能直接传入到另一个页面内,因为没有设置同个域名通用。
请看下面的函数解析:
setcookie(name,value,expire,path,domain,secure)
参数 描述
name 必需。规定 cookie 的名称。
value 必需。规定 cookie 的值。
expire 可选。规定 cookie 的过期时间。
time()+36002430 将设置 cookie 的过期时间为 30 天。如果这个参数没有设置,那么 cookie 将在 session 结束后(即浏览器关闭时)自动失效。
path 可选。规定 cookie 的服务器路径。
如果路径设置为 “/”,那么 cookie 将在整个域名内有效.如果路径设置为 “/test/”,那么 cookie 将在 test 目录下及其所有子目录下有效。默认的路径值是 cookie 所处的当前目录。【这个是存在多个cookie问题的原因】
domain 可选。规定 cookie 的域名。
为了让 cookie 在 example.com 的所有子域名中有效,您需要把 cookie 的域名设置为 “.example.com”。当您把 cookie 的域名设置为 www.example.com 时,cookie 仅在 www 子域名中有效。
secure 可选。规定是否需要在安全的 HTTPS 连接来传输 cookie。如果 cookie 需要在安全的 HTTPS 连接下传输,则设置为 TRUE。默认是 FALSE。
下面给出正确的设置:
function start_session($expire = 0) {
if ($expire == 0) {
$expire = ini_get('session.gc_maxlifetime');
} else {
ini_set('session.gc_maxlifetime', $expire);
}
$session_cache_dir = APP_PATH.'cache/tmp';
if(!file_exists($session_cache_dir)){
mkdir($session_cache_dir,0777,true);
}
ini_set('session.save_path',$session_cache_dir);
ini_set("session.cookie_httponly", 1);
//关键在这下面几句
if (!isset($_COOKIE['PHPSESSID'])) {
session_set_cookie_params($expire);//不存在cookie,则重新设置
session_start();
} else {
session_start();
//如果存在cookie,则将当前cookie里面存储的session_id更改时间,并将第三个参数传入'/'
//使之保持每个页面都是同一个cookie,参考上面的函数解析
setcookie('PHPSESSID', $_COOKIE['PHPSESSID'], time() + $expire,'/');
}
}
让cookie在同个域名下都是同一个,防止出现不同页面不同的cookie。然后前台传入的数据的时候携带cookie里的PHPSESSID,服务器读取到session_id就返回对应的数据。也就是重复设置session的过期时间,持续保持最新的cookie。
好了,问题解决了,也了解了session存储的机制。
如果看到这篇文章还有疑问的朋友,可以评论告诉我。