php SESSION和COOKIE实用详解
1、怎样理解cookie和session?
由于cookie和session关系密切,这里我们一并介绍。
打个比方:在河南时,我常到一家熟食店买馋嘴鸭,该店老板为了促进销售,特发布“每购满10只即可免费赠送一只”的优惠措施。除了家里有什么红白喜事要飨客之外,应该不会有人一次性购买10只烤鸭吧?所以老板得想个法子来记录顾客的消费数量,这里总共有三种方案:
Ⅰ、老板记住每一个顾客的消费数量,等到顾客消费满10只的时候自动奉送一只。这好比HTTP协议本身是有状态的,可以记住顾客的活动行为。但遗憾的是,出于种种考虑http协议本身是不能有状态的,老板自个也没有这么超常的记忆力,故这种方案行不通!
Ⅱ、老板发给顾客一张积分卡,上面记录着消费的数量,一般还有个有效期限。每次买烤鸭时,如果顾客出示这张卡片,老板就知道这位顾客曾经光顾过小店。这种做法就是在客户端保持状态,好比是cookie技术。打开(windows系统)C:\Documents and Settings\用户名\Cookies,你会发现一些*.txt格式的小文件,这就是你浏览某些网站,它们发给你的“积分卡”(cookies)。
Ⅲ、老板发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次买烤鸭时,如果顾客出示该卡片,则老板搬出店里的划名册,找到你的卡号并加1个积分。这种做法就是在服务器端保持状态。好比是session技术。
cookie 和session最大的区别在于: cookie是把积分卡发给顾客,上面记录了顾客所有的消费信息。Session则是把只有卡号(session id)的积分卡发给顾客,自家记录了顾客所有的消费信息。Cookie是保存在客户端的;session是保存在服务器端,而session id则是保存在客户端,通常也是一个cookie小文件,由于这个小文件除了session id(好比卡号)外什么也没有,因此比cookie安全多了。
COOKIE篇
如果你使用的是windows系统,那么请打开C:\Documents and Settings\用户名\Cookies,你会发现该目录下有好多*.txt格式的小文件。那就是cookie文件。当然,在该目录下你也可能什么都发现不了,那时因为你新装了系统或者从未浏览过因特网,也或者你的浏览器禁用了cookie。否则该目录下总会有点东西的。
Cookie注意事项
1、SetCookie()之前不能有任何html输出,它认了第二,没有哪个元素敢认第一,就是空格,空白行都不行。
2、 SetCookie()后,你在当前页调用echo $_COOKIE["name"]不会有输出。必须刷新或到下一个页面才可以看到Cookie值。原因很简单。SetCookie()执行之后,往客户端发送一个cookie,你不刷新或浏览下一个页面,客户端怎么把cookie给你送回去呀?浏览器创建了一个Cookie后,对于每一个针对该网站的请求,都会在Header中带着这个Cookie;不过,对于其他网站的请求Cookie是绝对不会跟着发送的。而且浏览器会这样一直发送,直到 Cookie过期为止。
3、使用Cookie的限制。一个浏览器能创建的Cookie数量最多为30个,并且每个不能超过4KB,每个WEB站点能设置的Cookie总数不能超过20个。 (这是书上看到的说法,应该是一个web站点能创建的Cookie不能超过30个吧,要不然,我机子里的cookie少说也上百了,请达人指教!)
4、 Cookie是保存在客户端的,用户禁用了Cookie,你的Cookie自然也就没作用啦!现在的浏览器,每当咱发送一个Cookie给客户端,他就像看门狗一样给拦截住了,并询问用户是否允许Cookie进门。天,用户又不是专家,有几个人知道啥叫Cookie呀?搞不好都当病毒拒之门外了。
SESSION篇
SESSION 的实现中采用COOKIE技术,SESSION会在客户端保存一个包含session_id(SESSION编号)的COOKIE;在服务器端保存其他 session变量,比如session_name等等。当用户请求服务器时也把session_id一起发送到服务器,通过session_id提取所保存在服务器端的变量,就能识别用户是谁了。同时也不难理解为什么SESSION有时会失效了。
当客户端禁用COOKIE时(点击IE中的“工具”—“Internet选项”,在弹出的对话框里点击“安全”—“自定义级别”项,将“允许每个对话COOKIE”设为禁用),session_id将无法传递,此时SESSION失效。不过php5在linux/unix平台可以自动检查cookie状态,如果客户端设置了禁用,则系统自动把session_id附加到url上传递。windows主机则无此功能。
2、Session常见函数及用法?
●Session_start():开始一个会话或者返回已经存在的会话。
说明:这个函数没有参数,且返回值均为true。如果你使用基于cookie的session(cookie-based sessions),那么在使用Session_start()之前浏览器不能有任何输出,否则会发生以下错误:
Warning: Cannot send session cache limiter - headers already sent (output started at /usr/local/apache/htdocs/cga/member/1.php:2)…………
你可以在php.ini里启动session.auto_start=1,这样就无需每次使用session之前都要调用session_start()。但启用该选项也有一些限制,如果确实启用了 session.auto_start,则不能将对象放入会话中,因为类定义必须在启动会话之前加载以在会话中重建对象。
请求结束后所有注册的变量都会被序列化。已注册但未定义的变量被标记为未定义。在之后的访问中这些变量也未被会话模块定义,除非用户以后定义它们。
警告: 有些类型的数据不能被序列化因此也就不能保存在会话中。包括 resource 变量或者有循环引用的对象(即某对象将一个指向自己的引用传递给另一个对象)
●session_id
session_id() 用于设定或取得当前session_id。php5中既可以使用session_id(),也可以通过附加在url上的SID取得当前会话的session_id和session_name。
如果session_id()有具体指定值的话,将取代当前的session_id值。使用该函数前必须启动会话:session_start();
当我们使用session cookies时,如果指定了一个session_id()值,每次启动session_start()都会往客户端发送一个cookie值。不论当前session_id是否与指定值相等。
session_id()如果没有指定值,则返回当前session_id();当前会话没有启动的话,则返回空字符串。
●检查session是否存在?
在以往的php版本中通常使用session_is_register()检查session是否存在,如果您使用$_SESSION[‘XXX’]=XXX来注册会话变量,则session_is_register()函数不再起作用。你可以使用isset($_SESSION[‘xxx’])来替代。
● 更改session_id session_regenerate_id() 更改成功则返回true,失败则返回false。
使用该函数可以为当前session更改session_id,但不改变当前session的其他信息。例如:
<?php session_start(); $old_sessionid = session_id(); session_regenerate_id(); $new_sessionid = session_id(); echo "原始 SessionID: $old_sessionid<br />"; echo "新的 SessionID: $new_sessionid<br />"; echo"<pre>"; print_r($_SESSION); echo"</pre>"; ?>
●session_name()返回当前session的name或改变当前session的name。如果要改变当前session的name,必须在session_start()之前调用该函数。注意:session_name不能只由数字组成,它至少包含一个字母。否则会在每时每刻都生成一个新的session id.
session改名示例:
<?php $previous_name = session_name("WebsiteID"); echo "新的session名为: $previous_name<br />"; ?>
●如何删除session?
1、unset ($_SESSION['xxx'])删除单个session,unset($_SESSION['xxx']) 用来unregister一个已注册的session变量。其作用和session_unregister()相同。 session_unregister()在PHP5中不再使用,可将之打入冷宫。
unset($_SESSION) 此函数千万不可使用,它会将全局变量$_SESSION销毁,而且还没有可行的办法将其恢复。用户也不再可以注册$_SESSION变量。
2、$_SESSION=array()删除多个session
3、session_destroy()结束当前的会话,并清空会话中的所有资源。。该函数不会unset(释放)和当前session相关的全局变量(globalvariables),也不会删除客户端的session cookie.PHP默认的session是基于cookie的,如果要删除cookie的话,必须借助setcookie()函数。
返回值:布尔值。
功能说明:这个函数结束当前的session,此函数没有参数,且返回值均为true
session_unset() 如果使用了$_SESSION,则该函数不再起作用。由于PHP5必定要使用$_SESSION,所以此函数可以打入冷宫了。
下面是PHP官方关于删除session的案例:
<?php // 初始化session. session_start(); /*** 删除所有的session变量..也可用unset($_SESSION[xxx])逐个删除。****/ $_SESSION = array(); /***删除sessin id.由于session默认是基于cookie的,所以使用setcookie删除包含session id的cookie.***/ if (isset($_COOKIE[session_name()])) { setcookie(session_name(), '', time()-42000, '/'); } // 最后彻底销毁session. session_destroy(); ?>
由此我们可以得出删除Session的步骤:
①session_start()
②$_SESSION=array()/unset($_SESSION['xxx'])
③session_destroy()
● SESSION安全:
会话模块不能保证存放在会话中的信息只能被创建该会话的用户看到。根据其存放的数据,还需要采取更多措施来主动保护会话的完整性。
评估会话中携带的数据并实施附加保护措施通常要付出代价,降低用户的方便程度。例如,如果要保护用户免于受简单的社交策略侵害(注:指在 URL 中显示的会话 ID 会被别人在电脑屏幕上看到,或被别的网站通过 HTTP Referer 得到等),则应该启用 session.use_only_cookies。此情形下,客户端必须无条件启用 cookie,否则会话就不工作。
有几种途径会将现有的会话 ID 泄露给第三方。泄露出的会话 ID 使第三方能够访问所有与指定 ID 相关联的资源。第一,URL 携带会话 ID。如果连接到外部站点,包含有会话 ID 的 URL 可能会被存在外部站点的 Referer 日志中。第二,较主动的攻击者可能会侦听网段的数据包。如果未加密,会话 ID 会以明文方式在网络中流过。对此的解决方式是在服务器上实施 SSL 并强制用户使用。
默认情况下,所有与特定会话相关的数据都被存储在由 INI 选项 session.save_path 指定的目录下的一个文件中。对每个会话会建立一个文件(不论是否有数据与该会话相关)。这是由于每打开一个会话即建立一个文件,不论是否有数据写入到该文件中。注意由于和文件系统协同工作的限制,此行为有个副作用,有可能造成用户定制的会话处理器(例如用数据库)丢失了未存储数据的会话。
上面介绍函数下文将会用到,但还有一些有关session的函数也介绍一下:
session_encode
函数功能:sesssion信息编码
函数原型:string session_encode(void);
返回值:字符串
功能说明:返回的字符串中包含全局变量中各变量的名称与值,形式如:a|s:12:"it is a test";c|s:4:"lala"; a是变量名 s:12代表变量a的值"it is a test的长度是12 变量间用分号”;”分隔。
session_decode
函数功能:sesssion信息解码
函数原型:boolean session_decode (string data)
返回值:布尔值
功能说明:这个函数可将session信息解码,成功则返回逻辑值true
Php5 不再使用session_id,而是把它变成一个常量SID,并保存在cookie中。如果客户端禁用了cookie,php会自动通过url自动传动传递SID,其条件是设置php.ini中的session.use_trans_sid = 1。此时即使客户端即使禁用了cookie也没关系了。
用 strip_tags() 来输出 SID 以避免 XSS 相关的攻击。
Session跨页传递问题:
session跨页传递需要考虑三种情况:
①客户端禁用了cookie。
②浏览器出现问题,暂时无法存取cookie
③php.ini中的session.use_trans_sid = 0或者编译时没有打开--enable-trans-sid选项
通过session id,可以取出定义的变量。跨页后,为了使用session,你必须又执行session_start();将又会产生一个session文件,与之对应产生相应的session id,用这个session id是取不出前面提到的第一个session文件中的变量的,因为这个session id不是打开它的“钥匙”。如果在session_start();之前加代码session_id($session id);将不产生新的session文件,直接读取与这个id对应的session文件。
PHP中的session在默认情况下是使用客户端的Cookie来保存session id的,所以当客户端的cookie出现问题的时候就会影响session了。必须注意的是:session不一定必须依赖cookie,这也是 session相比cookie的高明之处。当客户端的Cookie被禁用或出现问题时,PHP会自动把session id附着在URL中,这样再通过session id就能跨页使用session变量了。但这种附着也是有一定条件的,其一:“php.ini中的session.use_trans_sid = 1或者编译时打开打开了--enable-trans-sid选项”;其二:运行PHP的服务器必须是unix/linux系统,windows不具备此项功能。
解决session跨页传递问题的三条途径:
1、设置php.ini中的session.use_trans_sid = 1或者编译时打开打开了--enable-trans-sid选项,让PHP自动跨页传递session id。
2、手动通过URL传值、隐藏表单传递session id。
3、用文件、数据库等形式保存session_id,在跨页过程中手动调用。
下面举例说明:
第一种情况:
page1.php
<?php session_start(); $_SESSION['var1']="中华人民共和国"; $url="<a href="."\"s2.php\">下一页</a>"; echo $url; ?> page2.php <?php session_start(); echo "传递的session变量var1的值为:".$_SESSION['var1']; ?>
运行以上代码,在客户端cookie正常的情况下,应该可以在得到结果“中华人民共和国”。
现在你手动关闭客户端的cookie,再运行,可能得不到结果了吧。如果得不到结果,再“设置php.ini中的session.use_trans_sid = 1或者编译时打开打开了--enable-trans-sid选项”,又得到结果“中华人民共和国”
第二种途径:
s1.php
<?php session_start(); $_SESSION['var1']="中华人民共和国"; $sn = session_id(); $url="<a href="."\"s2.php?s=".$sn."\">下一页</a>"; //PHP5定义了一个常量SID来表示session_id(),$url还可以写成$url='<a href="page2.php?' . SID . '">下一页</a>'; echo $url; ?>
s2.php
<?php session_id($_GET['s']); session_start(); echo "传递的session变量var1的值为:".$_SESSION['var1']; ?>
第三种途径:
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=??????"> </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=="laogong" && $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(); } ?>