PHP中出现两个cookie或者session_id的深入解析及解决方案

这是我在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存储的机制。
如果看到这篇文章还有疑问的朋友,可以评论告诉我。

猜你喜欢

转载自blog.csdn.net/cherry_toto/article/details/91366424
今日推荐