PHP中如何保持SESSION以及由此引發的一些思考 最近的一個項目,裡面有一個比較大的表單,用戶完成它需要很多時間,很多用戶花了千辛萬苦完成之後,一提交發現SESSION過期,系統退出了,所以引起了研究如何設定SESSION以及保持SESSION在線的需要,以下是一些心得體會。
什麼是SESSION?
依照WIKI的解釋,SESSION是存在於兩個通訊設備間的交互訊息,在某一時間建立,經過一定的時間後失效。常見的SESSION有:TCP SESSION、WEB SESSION(HTTP SESSION)、LOGIN SESSION等。
根據OSI模型中,會話實現的位置不同,SESSION主要分為幾種,一種是應用層會話,包括WEB SESSION(HTTP SESSION)和telnet遠端登入session;會話層實現的,包括Session Initiation Protocol( SIP)和Internet Phone Call;在傳輸層實現的有TCP SESSION。
本文主要討論WEB SESSION,其一般有兩種:客戶端SESSION和伺服器端SESSION,後者最常見的屬於Java Beans提供的。
SESSION是做什麼的?
在電腦領域,特別是網路方面,SESSION使用的特別廣泛,也可以稱為是對話(Dialogue)、會話等,一般是指在兩個通訊設備間儲存的狀態,有時也發生在使用者和電腦之間(Login SESSION)。
區別於無狀態的通信,SESSION通常用來儲存通信狀態,因此通信的雙方至少有一方需要儲存SESSION的歷史記錄,從而實現兩者間的通信。
SESSION(WEB SESSION)是怎麼實現的?
瀏覽器和伺服器之間進行HTTP通訊時,通常會包含一個 HTTP Cookie 來識別狀態,通常會有一個唯一的 SESSIONID ,SESSION通常會記錄使用者的一些驗證資訊和層級。
在幾中程式語言中最常用的Http Session Token是,JSESSIONID(JSP),PHPSESSID(PHP),ASPSESSIONID(ASP),這個標識通常由雜湊函數產生,能夠唯一表示這個用戶的身份,在伺服器和客戶端通訊時,作為GET或POST的參數儲存在客戶端。
SESSION的實作方式通常有兩種,伺服器端SESSION和客戶端SESSION,兩種方式各有優缺點。
伺服器端SESSION實現容易且效率比較高,但是遇到負載平衡或高可用性需求的時候,處理起來就比較困難,對於那種內生系統不存在儲存設備的時候, 也是不可用的。負載平衡可以透過共享檔案系統或強制客戶只能登入一台伺服器來實現,但是這樣會降低效率。對於沒有儲存的設備,也可以透過使用 RAM(參考參考資料6)來解決伺服器端SESSION的實現,這種方法這對哪些客戶端連結有限的系統有效(諸如路由或接入點設備)。
客戶端SESSION的使用可以解決伺服器端SESSION的一些問題,例如避免了負載平衡的演算法等,但同時也會產生一些自身的問題。客戶端 SESSION使用Cookie和加密技術來在不同的請求之間保存狀態。在每一個動態頁面結束後,會統計目前的SESSION,並且把它傳回客戶端。每次成功 請求後,會把cookie再送到伺服器端,來讓伺服器「記起」這個使用者的身分。客戶端SESSION最重要的問題就是安全問題,一旦cookie被劫持 或竄改了,使用者的資訊的安全性就失去了。
PHP中如何設定SESSION?
搭建好PHP的開發環境後,透過phpinfo()可以查看到與SESSION相關的部分包括:
SESSION模組,在PHP V5.2.9版本中,總共有25個變數。其中,平常設定中常會用到的幾個有:
session.cookie_lifetime 设置存储SESSIONID的cookie过期时间 session.name SESSION的COOKIE名称,默认为PHPSESSID session.save_handler SESSION的存储方式,默认为FILE session.save_path Fedora下面默认存储在/var/lib/php/session session.gc_probability session.gc_divisor session.gc_maxlifetime 这三个选项用来处理GC机制发生的机率 session.cache_limiter (nocache,private,private_no_expire,public) session.cache_expire 这两个选项是用来缓存SESSION的页面
先來考慮第一個問題,SESSION多久會過期,他是如何過期的?如果要在PHP程式中使用SESSION,一定要先引用session_start(),這個函數一執行,就會在SESSION的儲存目錄(如果使用了file handler)產生一個SESSION文件,裡面內容是空的,同時瀏覽器會會見裡一個name為PHPSESSID的cookie,裡面存放著一個hash出來的SESSION的名字。
SESSION的過期依賴於一個垃圾回收機制(Garbage Collection),SESSION創建後作為一個文件存放在伺服器上,客戶端腳本每訪問一次SESSION中的變量,SESSION文件的訪問時間就會進行更新。每次存取都是根據客戶端儲存的SESSIONID去請求伺服器中儲存的唯一的SESSION,當客戶端的cookie過期後,就無法知道要存取的是哪一個SESSION,儘管此時伺服器上的SESSION檔案還沒有被過期收回,這樣就會造成伺服器資源的浪費。
但是同时,如果我们希望用户的session马上过期的话,我们就可以通过设置cookie的办法来实现。SESSION的回收是在每次访问页面的时候进 行的,回收的机率由session.gc_probability,session_gc_divisor指定,默认士1/100。如果设置为1,则每次 超过了SESSION的生存周期去访问的话,SESSION一定会被回收。
两种需求:
1、保持SESSION不过期或延长SESSION过期时间;
2、使SESSION立即过期。
1、保持SESSION不过期和延长SESSION过期时间非常必要,特别是在内部应用系统中或者有很大的表单的时候。想想你的老板在填写一个表单,刚好 碰上午饭时间,留着这个表单等吃饭回来,填写完剩余的内容,提交后他看到什么,一般来说都是一个登录界面。想要提高用户体验,关键是要让老板的表单不出问 题,我们就必须延长SESSION的生存周期。
保持SESSION不过期和延长SESSION过期时间,可以通过设置session.gc_maxlifetime来实现,不过首先需要保证客户端的 cookie不会在gc执行回收之前失效。通过设置一个较长的gc_maxlifetime可以实现延长session的生存周期,可是对于不是所有请求 都会保持很久的应用来说,这么做对于服务器配置显然不是一个最佳的选择。
我们知道SESSION的回收机制是根据SESSION文件的最后访问时间来判断的,如果超过了maxlifetime,则根据回收机率进行回收。所以我们只需要定期的去访问一下SESSION就可以了,而这可以通过刷新页面来实现,根据这个思路,解决的方法就有了。
通过JS定期的去访问页面;
利用Iframe定期的刷新页面;
直接利用程序发送HTTP请求,这样就可以避免在页面中嵌入其他的元素;
下面是利用JS发送请求实现的保持SESSION不过期的实现方法,这样我们就只需要在需要SESSION保持长时间的页面(比如大表单页面)。
<script type=”text/javascript”> function keepMeAlive(imgName){ myImg = document.getElementById(imgName); if(myImg) myImg.src = myImg.src.replace(/\?.*$/, ‘?' + Math.random()); } window.setInterval(“keepMeAlive(‘phpImg');”, 4000); </script>
2b0523fb125092089a05bc06b76d15ca
其中URL后加入一个随机数是为了避免这个链接的请求被浏览器缓存。
2、使SESSION立即过期的方法就比较多了,我们可以session_destroy(),也可以用上面的思路,请求一个session_destroy的页面。
SESSION安全吗?
PHP的手册中明确写出:SESSION并不能保证储存在SESSION中的信息一定只能被他的创建者所看到。
如果想要安全的处理一些远程的操作,那么HTTPS是唯一的选择。最基本的,不要认为一个用户信息在SESSION中存在就认为这个用户一定就是他本人, 虽然SESSION中的信息会给你他已经经过了用户名和密码验证的假象。所以,如果需要做一些修改密码或者类似的事情的时候,让用户重新输入密码是一个比 较好的选择。
早期的Apache版本并没有采用COOKIE的方式来存储PHPSESSID,而是采用的URL-rewrite,也就是每个URL后面都会加上 PHPSESSID=6570879aef85ef8e10f1df5310b71b95来表明它属于那个激活的SESSION,新版的Apache已经将这个属性设置为默认关闭。
session.use_trans_id = 0;
所以从这个意义上来讲,延长SESSION的时间过长或者保持SESSION一直在线对于安全来说始终不是一件好事情。终极的解决办法就是用户提交跳转到 登录窗口,登录后又能够回到填写页面,并且所有的数据都还在。这个的实现方式现在用Ajax来解决应该没什么困难,每隔一定时间就把当前的用户数据 POST到一个存储位置,不管是XML或者JSON。
拾遗:
对于客户端不支持JavaScript的情况可以采用的方法:
1、写一个浮层,显示在最顶层,如果用户未禁用JS,则让浮层消失;
2、将所有的INPUT都设置为disable,然后再用JS设置为enabled;
以上这两种方式都是在JS被禁用的时候,所有功能都不能用,如何在JS被禁用的情况下使我们的应用仍然正常工作,这个貌似就比较困难。实现这个的所花的时间和所收到的效果大家要权衡一下。
更多PHP中怎样保持SESSION不过期 原理及方案介绍相关文章请关注PHP中文网!