在PHP中使用过SESSION的过程中,可能会碰到这么一个问题,SESSION变量不能跨页传递。这令我苦恼了好些日子,最终通过查资料思考并解决了这个问题。我认为,出现这个问题的原因有以下几点:
1、客户端禁用了cookie
2、浏览器出现问题,暂时无法存取cookie
3、php.ini中的session.use_trans_sid = 0或者编译时没有打开--enable-trans-sid选项
针对这种情况,我们可以提供两种解决思路:
1、网页做cookie是否禁用的检测,如果禁用了提示用户开启cookie再继续使用。
2、不做检测和提示,换跑道直接规避出现这种情况的可能性。即,不使用cookie来记录session_id
一、PHP中的SESSION机制说明:
在默认情况下,当访客访问网站时,php程序通过预先写好的session_start();命令在服务器端为该访客创建一个session文件,用于存储该访客的变量。
并将创建好的session文件的id发送给客户端,客户端浏览器收到该session_id时,将该session_id存储在浏览器本地cookie中。
当访客离开该网页进入其他网页时,浏览器从本地cookie中取出session_id再发送给服务端,服务端php执行到session_strart()时将不再创建新的session给该访客,而是根据该session_id获得该访客的session变量继续使用,在各个跨网页间进行变量传递,已达到跨网页传递数据和用户信息不丢失的目的。
但是,当访客的客户端浏览器如果禁用了cookie,客户端将无法保存session值,当用户离开该网页进入其他网页时,服务端无法获得session_id和继续使用该session文件,就会把访客当做新用户,再次创建一个session文件给其使用。这样以来session就无法做到跨网页传递数据的目的了。
表现出来的现象就是session失效无法跨网页传递数据。
session id可以使用客户端的Cookie或者Http1.1协议的Query_String(就是访问的URL的“?”后面的部分)来传送给服务器,并非只能使用cookie。
二、对该问题的解决方法:
1、做cookie是否禁用的检测,如果禁用了提示用户开启后再使用。
这里使用js来检测客户端浏览器cookie是否禁用。并提示
这里为了避免js也被禁用,也做了js是否禁用的检测和提示。
<style> .close{display:none;color:red;font-size:16px;font-weight:bold;text-align:center;} .show{color:red;font-size:16px;font-weight:bold;text-align:center;} </style> <div class="show" id="NoJs">你禁用了javascript,请开始!否则无法使用后台!</div> <div class="close" id="NoCookie">你禁用了cookie,请开始!否则无法使用后台!</div> <script> //检测是否禁用了js var NoJs= document.getElementById("NoJs"); NoJs.className="close"; //检测是否禁用了cookie function CookieEnable(){ var result=false; if(navigator.cookiesEnabled) return true; document.cookie = "testcookie=yes;"; var cookieSet = document.cookie; if (cookieSet.indexOf("testcookie=yes") > -1) result=true; document.cookie = ""; return result; } if(!CookieEnable()){ //alert("对不起,您的浏览器的Cookie功能被禁用,请开启"); var NoCookie= document.getElementById("NoCookie"); NoCookie.className="show"; } </script>
2、不使用cookie存储session_id的方法:
(1)、开启透明SID
需要修改的php.ini是:
session.use_trans_sid = 1 //由0改为1
session.use_only_cookies = 0 //是否只使用cookie来保存session值 该参数为1时,上述机制失效。
session.use_cookies = 0 //设置客户端是否使用cookie来保存session值 该参数的值不影响上述机制的进行。这个可改可不改
当在php开启了透明SID后(也就是自动模式),系统会将url后面自动添加PHPSESSID参数。也就是URL的格式为:
http://baidu.com/index.php?PHPSESSID=dsfwer23r23ed
每次自动将sessionid以get参数形式添加到url上,在每个页面直接获得get参数获得sessionid并使用即可
<?php if($_GET["PHPSESSID"]){ session_id($_GET["PHPSESSID"]);//这步必须在session_start()之前获取并确定sessionid } session_start(); echo session_id().'<br/>';
(2)、如果没有修改php.ini配置文件权限,可使用手动GET传sessionid、隐藏表单传递sessionid、服务端将sessionid保存在文件中读写、将sessionid保存在数据库中。这四种方案都可以解决。
A、手动GET传值
s1.php
<?php session_start(); $_SESSION[’var1’]="中华人民共和国"; $sn = session_id(); $url="<a href=".""s2.php?PHPSESSID=".$sn."">下一页</a>"; echo $url; ?>
s2.php
<?php session_id($_GET['PHPSESSID']); //设置session_id必须要在session_start()前 session_start(); echo "传递的session变量var1的值为:".$_SESSION[’var1’]; ?>
B、隐藏表单传递session_id
在form表单中,增加
<?php $sn = session_id(); ?> <input type="text" name="PHPSESSION" value="<?php echo $sn;?>">
C、服务端将session保存在文件中。
login.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Login</title> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> </head> <body> 请登录: <form name="login" method="post" action="mylogin1.php"> 用户名:<input type="text" name="name"><br> 口 令:<input type="password" name="pass"><br> <input type="submit" value="登录"> </form> </body> </html>
mylogin1.php
<?php $name=$_POST[’name’]; $pass=$_POST[’pass’]; if(!$name || !$pass) { echo "用户名或密码为空,请<a href="login.html">重新登录</a>"; die(); } if (!($name=="youngong" && $pass=="123") { echo "用户名或密码不正确,请<a href="login.html">重新登录</a>"; die(); } ob_start(); session_start(); $_SESSION[’user’]= $name; $psid=session_id(); $fp=fopen("e:/tmp/phpsid.txt","w+"; fwrite($fp,$psid); fclose($fp); //身份验证成功,进行相关操作 echo "已登录<br>"; echo "<a href="mylogin2.php">下一页</a>"; ?>
mylogin2.php
<?php $fp=fopen("e:/tmp/phpsid.txt","r"; $sid=fread($fp,1024); fclose($fp); session_id($sid); session_start(); if(isset($_SESSION[’user’]) && $_SESSION[’user’]="laogong" { echo "已登录!"; } else { //成功登录进行相关操作 echo "未登录,无权访问"; echo "请<a href="login.html">登录</a>后浏览"; die(); } ?>
注:这里可以将每个txt文件名用会员的id命名,方便读写又区分不同用户。
D、将sessionid保存在数据库中。
简单说就是当用户登录成功时,控制器获得了该用户的会员信息和sessionid,将sessionid保存到数据库表中。
当用户进入其他页面时,通过查询数据表中该会员id关联的sessionid来获得session值。如果session失效,则无法获得session值则需要重新登录。
这里就不举例了。
三、种特殊情况:就是服务器配置方面有问题,session文件的保存路径是没有读写权限的,导致session文件无法创建。
解决方法:
1、先建立一个phpinfo文件,查看下你的服务器session存放路径。
phpinfo.php
<?php phpinfo(); ?>
运行该文件后查看 session.save_path 的值就是存放路径。
2、测试该存放session文件的路径是否有读写权限:
写一个文件:test.php来测试一下:
<? echo var_dump(is_writeable(ini_get(“session.save_path”))); ?>
如果返回bool(false),证明文件夹写权限被限制了,你可以给加上写入权限或者在PHP程序中指定一个文件夹存放session。
PHP中指定session存放路径:
//设置当前目录下session子文件夹为session保存路径。 $sessSavePath = dirname(__FILE__).’/session/’; //如果新路径可读可写(可通过FTP上变更文件夹属性为777实现),则让该路径生效。 if(is_writeable(sessSavePath) && is_readable(sessSavePath) && is_readable(sessSavePath)) { session_save_path($sessSavePath); }else{ echo $sessSavePath." 指定session路径不可读写,请修改权限为777"; }
四、种特殊情况:Bom头原因导致Cookie无法送出。
登录成功后又跳转到登录页面,在提交信息后输出session都是正常的,没有问题,但是页面跳转后,session出现丢失现象,无法正常完成登陆。
通过查找资料,发现原来是bom头的原因。受COOKIE送出机制的限制,在这些文件开头已经有BOM的文件中,COOKIE无法送出(因为在COOKIE送出前PHP已经送出了文件头),所以登入和登出功能失效。一切依赖COOKIE、SESSION实现的功能全部无效。
正确的处理方法是去掉某些文件的bom,一般情况是在入口文件出现的bom问题,我处理的方法是用Notepad++打开文件,格式选择以UTF-8无bom格式编码,然后保存,重新上传到服务器即可。但一定要注意,去掉bom上传到服务器之前需要把服务器上源文件删掉,上传覆盖不能去掉bom。
PS:如果文件是UTF-8编码,则确保所有文件都是UTF-8 不能用UTF-8 + BOM
好了,通过这篇文章,我们就对session跨网页失效的原因和解决方案做了详细的说明。
碰到类似奇怪问题,就迎刃而解了。
如果对PHP的Session使用还有不清楚的地方,可参见之前的文章:
评论回复