筆記整理--Http-Cookie

如何設置一個永遠沒法刪除的Cookie -- 系統架構 -- IT技術博客大學習 -- 共學習 共進步! - Google Chrome (2013/6/20 9:46:38)

如何設置一個永遠沒法刪除的Cookie

    出處信息

   在網站統計中,咱們最經常使用的是用 Cookie標識身份,因爲瀏覽器自帶的 Cookie容易被用戶刪除。因而不少人使用 Flash Cookie來跟蹤用戶的信息。可是在目前360等軟件幫助下,刪除Flash Cookie也變得很是的簡單。那麼有沒有什麼方法讓Cookie沒法刪除呢?答案是有的!作開發的基本上都理解災備機制。即一臺服務器若是出現了故障,則可由由另外一臺恢復回去。好比Cookie一旦刪除後,這可經過Flash Cookies進行恢復。另外,除了Cookie和Flash Cookie外,到底還有哪些方式能夠用來進行「用戶識別」。javascript

   一、標準的 Http Cookiephp

   HTTP Cookie是最常的用於「用戶識別」的方式,如下爲服務器與Cookie之間的交互流程:css

  • 當咱們用瀏覽器訪問一個網站的時候,就會想服務器發起一個請求。html

  • 根據請求的url和cookie自己的屬性,篩選出須要發送給服務器的cookie(固然也有可能沒有cookie)。html5

  • 若是有多個cookie的話,還會決定發送的順序。java

  • 把這些須要發送的cookie包含在HTTP的包頭裏發送給服務器。jquery

  • 而後到了應答階段,服務器會發回一個應答包頭,包頭裏包含了cookie信息。nginx

  • 瀏覽器解析這個cookie,包括名稱,值,路徑等元素。git

  • 最後,把cookie保存在本地。程序員

  •    至於哪些cookie會被髮送到服務器端,是有一套規則的,例如域名選擇、路徑選擇和Max-Age選擇,這些均可以在RFC2109裏找到。

       每次的http請求,cookie都會包含在包頭裏發送給服務器,這也是被開發者廣爲詬病的一個cookie缺點,由於這意味這每一個請求都無形中增長了流量,特別是像請求圖片這些資源的時候,附帶的cookie信息是徹底沒有必要的。因此如今不少網站圖片等靜態資源都會用獨立的域名運做,這樣就能夠單獨對這些域名進行cookie設置。 除此之外,cookie還有如下影響比較大的缺點:

  • 安全性問題。cookie在http請求中是明文傳遞的,除非使用SSL通道,否則是不宜在cookie裏放置重要信息。

  • 大小問題。每一個cookie的大小不能超過4K。每一個Domain下cookie個數根據瀏覽器不一樣也不一樣。

  •    關於Cookies的一些限制問題,能夠參考下Nicholas的一篇文章: 瀏覽器容許的每一個域名下的Cookie數:

  • IE7跟IE8限制爲50個。

  • Firefox限制爲50個。

  • Opera限制30個

  • Safari/WebKit沒有限制,可是若是header的大小超過服務器能處理的狀況下,則會出現錯誤。

  •    那若是Cookie數設置超過限制的時候,各瀏覽器又是如何處理呢:

  • Safari因爲沒有Cookie數的限制,因此不做討論。

  • 在IE和Opera下,將會採用LRU(The Least Recently Used)方法,自動踢掉最老的cookie以騰出空間給新的cookie。IE和Opera使用這個方法。

  • Firefox就比較獨特:它貌似會隨機決定哪些cookie將會保留,儘管最後設置的cookie會被保留。因此在Firefox裏不要超過cookie數的限制。

  •    cookie的總大小在各瀏覽器中也是不一樣的:

  • Firefox和Safari容許cookie多達4097個字節,其中4096個字節是名字和值,1個字節是=號。

  • Opera容許cookie多達4096個字節,這些字節包含名字、值和=號。

  • IE容許4095個字節,這些字節包含名字、值和=號。

  •    注意這裏用的字節,也就是,若是是多字節字符,天然就會佔用兩個字節。在全部瀏覽器裏,若是設置的cookie大小超過限制,那麼它就會被忽略或者不被設置。

       從上面,咱們能夠看到,Cookie確實存在一些不足,可是它的一些缺點也正是它的優勢,例如每一個請求都會被放到包頭裏發送給服務器,正是這個特性咱們才能很方便的傳輸sessionid。Cookie的出現可謂大大推進了網頁的發展,並且在將來很長的一段時間裏,Cookie還會繼續發揮它的做用。可是也正是因爲Cookie存在種種的不足,纔會有新的本地存儲技術出現的需求。

       二、Local Shared Objects (Flash Cookies)

       Local Shared Objects 即本地共享對象,長被稱爲Flash Cookie。Flash Cookies是由Adobe公司開發的一個技術,該技術容許Flash對象在每一個域名上存儲100KB的數據。LSO解決了Cookie的一些問題,例如大小,安全等。跟Cookie不一樣,LSO被保存爲二進制文件(不過變量名具備可讀性)。LSO具備了很多優勢,可是缺點也是明顯,就是它須要安裝Flash這個插件。雖然如今Flash的普及率很高,可是這種依賴插件的技術始終不能解決問題的根源,並且爲了使用這個方案不得不引入額外的swf和js文件。另外IE8開始和Chrome在刪除歷史記錄的時候會將Flash Cookie刪除掉。

       三、Silverlight Isolated Storage

       獨立存儲(Isolated Storage)是Silverlight 2中提供的一個客戶端安全的存儲,它是一個與Cookie機制相似的局部信任機制。獨立存儲機制的APIs 提供了一個虛擬的文件系統和能夠訪問這個虛擬文件系統的數據流對象。Silverlight中的獨立存儲是基於 .NET Framework中的獨立存儲來創建的,因此它僅僅是.NET Framework中獨立存儲的一個子集。

       Silverlight中的獨立存儲有如下一些特徵:

  • 每一個基於Silverlight的應用程序都被分配了屬於它本身的一部分存儲空間, 可是應用程序中的程序集倒是在存儲空間中共享的。一個應用程序被服務器賦給了一個惟一的固定的標識值。基於Silverlight的應用程序的虛擬文件系統如今就以一個標識值的方式來訪問了。這個標識值必須是一個常量,這樣每次應用程序運行時才能夠找到這個共享的位置。

  • 獨立存儲的APIs 其實和其它的文件操做APIs相似,好比 File 和 Directory 這些用來訪問和維護文件或文件夾的類。 它們都是基於FileStream APIs 來維護文件的內容的。

  • 獨立存儲嚴格的限制了應用程序能夠存儲的數據的大小,目前的上限是每一個應用程序爲1 MB。

  •    四、IE瀏覽器 userData 存儲   (從IE9開始, 再也不支持 userData)

       userData是微軟在第一次瀏覽器大戰中的產物,屬於DHTML中的一種技術。相比起Cookie,userData在每一個域名下可存儲達的數據提高了很多,可是具體的大小視domain的安全域而定。userData的數據會一直存在,直到被刪除或者到過時時間。而且基於安全的考慮,一個 userData 存儲區只能用於同一目錄和對同一協議進行存儲。userData在數據的本地儲存來講,比cookie進步了很多,可是它有個致命的缺點:僅支持IE。僅憑這一點,就註定了userData並不會有太大的做爲,只能用做配合其餘本地存儲技術兼容低版本的IE。

       五、利用 HTTP ETags 存儲Cookie

       Etag 是URL的Entity Tag,用於標示URL對象是否改變,區分不一樣語言和Session等等。具體內部含義是使服務器控制的,就像Cookie那樣。

       HTTP協議規格說明定義ETag爲「被請求變量的實體值」 。另外一種說法是,ETag是一個能夠與Web資源關聯的記號(token)。典型的Web資源能夠一個Web頁,但也多是JSON或XML文檔。服務器單獨負責判斷記號是什麼及其含義,並在HTTP響應頭中將其傳送到客戶端。

       六、在瀏覽器歷史記錄中存儲cookie

       你們都知道,用戶訪問過一次頁面,就會存儲在瀏覽器瀏覽歷史裏面,這個方法就是利用瀏覽器的這個特性。經過新建一個iframe去訪問這個頁面。如默認的url是http://www.example.com/test/。

       那他發送的路徑會是加上了name跟value的。這裏的name跟value分別是id跟123456,如

       http://www.example.com/test/id/123456

       發送方法爲每一個字母遞增發送,並在最後加個」-「的結束符號

       http://www.example.com/test/i

       http://www.example.com/test/id

       …

       http://www.example.com/test/id/123456

       http://www.example.com/test/id/123456-

       那要相應的name value他是這樣獲取的。

       默認url加上 」ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=-「。其中一個字符,看看是否存在歷史記錄裏面。不存在則循環查找下一個。若是這裏查到i是訪問過的 ,則繼續循環,在i的後面循環檢查。繼而又查到d是訪問過的。一直循環知道出現’-‘符號爲止。繼而解析獲取到的字符串,那name value天然也就解析出來。

       但這樣作的弊端很大。首先,必需要連續發送n個url,用戶體驗很差。獲取的時候要遍歷,也影響了瀏覽器的性能。因此不推薦。

       七、使用PNG像素圖片保存Cookie

       以自動生成、強制緩存的PNG像素圖片的RGB值形式保存cookie,使用HTML5 Canvas標籤讀取像素圖片(cookie)。

       服務器建立一個寬100像素高1像素的黑色空白PNG(每一個像素的RGB 顏色可存儲3個字節,可存儲600字節信息),而後將值拆分並按順序每3個字母生成一個RGB顏色值而且按順序設置到圖片的像素點中,而後給這個圖片設置一個expires很是長的時間(Expire 頭,用於客戶端緩存,不一樣於cookie的expire屬性)讀取的時候取出而且解析還原出來。要求瀏覽器必須支持html5才能用上此方法。ie8,ie9,ff,chrome,safari都是 ok的。

       八、使用window.name儲存Cookie

       window.name 傳輸技術,本來是 Thomas Frank 用於解決 cookie 的一些劣勢(每一個域名 4 x 20 Kb 的限制、數據只能是字符串、設置和獲取 cookie 語法的複雜等等)而發明的(詳細見原文:《Session variables without cookies》),後來 Kris Zyp 在此方法的基礎上強化了 window.name 傳輸 ,並引入到了 Dojo (dojox.io.windowName),用來解決跨域數據傳輸問題。

       window.name 的美妙之處:name 值在不一樣的頁面(甚至不一樣域名)加載後依舊存在,而且能夠支持很是長的 name 值(2MB)。

       name 在瀏覽器環境中是一個全局/window對象的屬性,且當在 frame 中加載新頁面時,name 的屬性值依舊保持不變。經過在 iframe 中加載一個資源,該目標頁面將設置 frame 的 name 屬性。此 name 屬性值可被獲取到,以訪問 Web 服務發送的信息。但 name 屬性僅對相同域名的 frame 可訪問。這意味着爲了訪問 name 屬性,當遠程 Web 服務頁面被加載後,必須導航 frame 回到原始域。同源策略依舊防止其餘 frame 訪問 name 屬性。一旦 name 屬性得到,銷燬 frame 。

       在最頂層,name 屬性是不安全的,對於全部後續頁面,設置在 name 屬性中的任何信息都是可得到的。然而 windowName 模塊老是在一個 iframe 中加載資源,而且一旦獲取到數據,或者當你在最頂層瀏覽了一個新頁面,這個 iframe 將被銷燬,因此其餘頁面永遠訪問不到 window.name 屬性。

       window.name 傳輸技術相比其餘的跨域傳輸的一些優點:

  • 它是安全的。也就是說,它和其餘的基於安全傳輸的 frame 同樣安全,例如 Fragment Identifier messaging (FIM)和 Subspace。(I)Frames 也有他們本身的安全問題,因爲 frame 能夠改變其餘 frame 的 location,可是這個是很是不一樣的安全溢出,一般不太嚴重。

  • 它比 FIM 更快,由於它不用處理小數據包大小的 Fragment Identifier ,而且它不會有更多的 IE 上的「機關槍」聲音效果。它也比 Subspace 快,Subspace 須要加載兩個 Iframe 和兩個本地的 HTML 文件來處理一個請求。window.name 僅須要一個 Iframe 和一個本地文件。

  • 它比 FIM 和 Subspace 更簡單和安全。FIM 稍微複雜,而 Subspace 很是複雜。Subspace 也有一些額外的限制和安裝要求,如預先聲明全部的目標主機和擁有針對若干不一樣特殊主機的 DNS 入口。window.name 很是簡單和容易使用。

  • 它不須要任何插件(好比 Flash)或者替代技術(例如 Java)。

  •    九、使用HTML5客戶端儲存數據方法。

       HTML5 提供了四種在客戶端存儲數據的新方法,即

  • HTML5 Session Storage

  • HTML5 Local Storage

  • HTML5 Global Storage

  • HTML5 Database Storage via SQLite

  •    HTML5 Session Storage顧名思義它就如同Session。 針對一個 session 的數據存儲,任何一個頁面存儲的信息在窗口中同一網站的任何頁面均可以訪問它存儲的數據。每一個窗口的值都是獨立的,它的數據會因窗口的關閉而丟失,不一樣窗口間的sessionStorage是不能夠共享的。

       HTML5 Local Storage 沒有時間限制的數據存儲,次日、第二週或下一年以後,數據依然可用。也就是說,localStorage是永遠不會過時的,除非主動刪除數據。數據可跨越多個窗口,無視當前會話,被共同訪問、使用。

       HTML5 Global Storage 同HTML5 Session Storage同樣,區別在於其在瀏覽器關閉後,能夠將存儲的信息仍可以保留下來。目前只有FireFox支持,且只支持當前域下的存儲。

       HTML5 Database Storage via SQLite (目前只谷歌瀏覽器支持):能夠理解成一個Html5環境下能夠用Js執行CRUD的Web數據庫。對於簡單的數據,使用sessionStorage和localStorage可以很好地完成存取,可是對於處理複雜的關係型數據,它就力不從心了。這也是 HTML 5 的「Web SQL Database」API 接口的應用所在。

       evercookie

       一些常見的用來標註用戶身份的方法已經介紹過了,接下來要講解的是如何使用。Evercookie是一個JavaScript API,經過它能夠在瀏覽器中生成極其持久的cookie。它的目標就是在用戶刪除了傳統cookie、Flash cookie(LSO)和其餘緩存數據後,仍然能夠識別客戶端。Evercookie是經過利用瀏覽器不一樣的存儲機制,把cookie數據保存在多個不一樣的地方實現的。此外,若是發現用戶刪除了其中一些cookie,Evercookie會利用這些機制從新建立它們。

       雖然Evercookie很是的強大,可是我的以爲大部分功能都比較花俏。如evercookie_png,evercookie_etag,evercookie_history,實際上這些操做均會對用戶體驗有必定影響。不過也體現了evercookie的宗旨:爲了記錄用戶信息,無所不用其極。但針對於國內用戶大部分爲ie用戶,並且不少網站是ie only,徹底可把evercookie_userdata歸入考慮範圍。同時通常狀況下,裝有silverlight插件的瀏覽器,幾乎必然也裝着flash插件。因此首選flash方案。

       Evercookie被認爲是一項很邪惡的技術,事實上做者Kamkar的座右銘是「think bad, do good」。Kamkar說,他寫evercookie是爲了向用戶展現公司能夠跟蹤他們的方法。

       「我但願evercookie只是向人們演示跟蹤他們的是何種方法,由他們決定他們是否應該阻止這些方法,我做爲一個安全業餘愛好者,寫evercookie也才用了不到一天的時間,所以我很容易想像那些受僱的開發者能夠作出什麼來。」

       有證據代表,Hulu、AOL和Spotify等網站已經開始在本身的網站上使用EverCookies。

       參考連接:

       http://en.wikipedia.org/wiki/HTTP_cookie

       http://en.wikipedia.org/wiki/Zombie_cookie

       http://samy.pl/evercookie/

       https://github.com/samyk/evercookie

淺析http協議、cookies和session機制、瀏覽器緩存 -- 網絡系統 -- IT技術博客大學習 -- 共學習 共進步! - Google Chrome (2013/5/30 17:38:05)

淺析http協議、cookies和session機制、瀏覽器緩存

    出處信息

最近幾天在複習http協議中headers,cookies、session、緩存等相關知識,發現些新知識點。

這篇文章注重結合PHP去理解這些內容,也就是比較注重實踐部分。

http headers          

NO1對於web應用,用戶羣在客戶端 (各類瀏覽器)點擊任何一個鏈接向服務器發送http請求,這過程確定須要3次握手,創建鏈接,服務器響應返回數據。

每次請求都有頭部和實體部分,先看下面筆者監聽QQ空間的headers,QQ空間的緣由是它頭部內容比較全

  1. Request Headers:  


  2. GET http://user.qzone.qq.com/445235728 HTTP/1.1  


  3. Host: user.qzone.qq.com  


  4. Connection: keep-alive  


  5. Cache-Control: max-age=0


  6. User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11  


  7. Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8  


  8. Referer: http://qzone.qq.com/  


  9. Accept-Encoding:gzip,deflate,sdch  


  10. Accept-Language: zh-CN,zh;q=0.8  


  11. Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3  


  12. Cookie:o_cookie=445235728;(省略不少……)  


  13. If-Modified-Since: Wed, 13 Jun 2012 01:32:19 GMT  


  14. -----------------  


  15. Response Headers:  


  16. HTTP/1.1 200 OK  


  17. Connection:close  


  18. Server: QZHTTP-2.34.0  


  19. Date: Wed, 13 Jun 2012 02:59:31 GMT  


  20. Content-Encoding: gzip  


  21. Set-Cookie:login_time=61F0EEA02D704B1DBCF25166A74941B24F4BE24B205C466F;PATH=/;DOMAIN=qzone.qq.com  


  22. Set-Cookie:Loading=Yes;expires=Wed,13-Jun-201216:00:00GMT;PATH=/;DOMAIN=qzone.qq.com X-UA-Compatible: IE=EdgeLast-Modified: Wed, 13 Jun 2012 02:59:31 GMT  


  23. Cache-Control: max-age=0, no-transform  


  24. Content-Type: text/html;charset=utf-8  


  25. Transfer-Encoding: chunked  


客戶端向服務端發請求headers和服務端響應客戶端headers圖:

經過圖片能夠看出:

一、客戶端請求headers包含了請求行和一些頭域。

請求行:請求的方法 統一資源標識器(URL)協議版本 ------這三者用空格分開,最後換行回車(\r\n) 例如:GET http://user.qzone.qq.com/445235728 HTTP/1.1

各類頭域:這些頭域都是有關鍵字和鍵值成對組合,最後換行回車(\r\n)結束,這些頭域告訴服務器應該怎麼去響應以及自己一些信息。

二、服務器響應

狀態行:協議版本 響應狀態 狀態描述 ------這三者用空格分開,最後換行回車(\r\n) 例如:HTTP/1.1 200 OK

各類頭域:這些頭域也是有關鍵字和鍵值成對組合,最後換行回車(\r\n)結束,這些頭域告訴客戶端應該怎麼去響應以及自己一些信息。

NO2

這裏就不一一說每一個頭域的概念和做用,想了解的請看:http://www.phpben.com/?post=34如今介紹幾個認爲重要、在一些網站上的測試數據、以及請求返回各頭域php代碼實現

測試時間:2012.6.14前

測試對象:csdn 、cnbeta 、cnblos、騰訊(QQ空間、朋友網、新聞網)、新浪(微博、主頁)、人人網、百度、淘寶、優酷、土豆這些網站

(1)Connection頭域:這個頭域只有http/1.1纔有,默認是keep-alive值表示長鏈接,這樣的話就不用每請求一個資源好比圖片,css文件,js文件都要和服務器進行3此握手鍊接,這個在必定程度上彌補了http沒狀態的一個缺陷,減小鏈接服務器的時間。

查看測試網站Connection頭域發現騰訊QQ空間、騰訊新聞網、新浪主頁和微博,優酷和土豆Connection:close;除了這些其餘的都是Connection:keep-alive

爲何?

一、connection: keep-alive 能正常使用的一個前提條件是還要提供content-length的頭域告訴客戶端正文的長度。那計算正文長度是個問題,對於多內容,集羣服務器來講不是件易事。騰訊和新浪,優酷的這些都很難計算,對與工程師來講之間關閉了(默認是打開的)。

二、老服務器端不支持,對於騰訊,新浪這些老油條,服務器集羣很龐大,不免有些老舊的不支持長鏈接的,爲了一些兼容性問題,直接關閉了

Ps:這兩點緣由未求證過!^-^

用php headers(「Connection:keep-alive」);

(2)Content-Encoding頭域

Content-Encoding文檔的編碼(Encode)方法.

上述網站出了cnbeta不用gzip壓縮,優酷用deflate,其他都是。這也透漏一個重要信息,那就phper要掌握壓縮gzip傳輸技術。

Php能夠經過mod_gzip模塊來實現。代碼:ob_start("ob_gzhandler");

(3)Server頭域暴漏服務器重要的安全信息。

Csdn:Server:nginx/0.7.68  ------------版本都暴露

騰訊QQ空間:Server:QZHTTP-2.34.0--------某位tx朋友透漏這是內部本身開發的服務器,這個可夠安全

新浪微博:Server:apache -------------這個沒暴漏版本

鳳凰網:Server: nginx/0.8.53

人人網:Server:nginx/1.2.0

淘寶網:Tengine ---------這是淘寶內部技術團隊開發的web服務器,基於Nginx

cnblogs博客園:Server:Microsoft-IIS/7.5

騰訊朋友網:Tencent/PWS ---------騰訊內部開發

騰訊新聞網:Server:squid/3.1.18

優酷網:Server: fswww1-----------是否是內部就不清楚,至少筆者不知道什麼來的^_^

土豆網:Tengine/1.2.3

百度:server: BWS/1.0 ---------應該也是百度內部本身開發的服務器

很明顯Server頭域是返回服務器的信息,但也能夠說暴漏信息,面對這個問題,大公司就本身開發基於本身功能的內部服務器。

(4)X-Powered-By頭域可供修改,基於安全則能夠修改

X-Powered-By頭域反應什麼語言什麼版本執行後臺程序。這個能夠同個header函數修改

header("X-Powered-By:acb");

(5)Cache-control、expires、last-modified等重要頭域

Cache-control:指定請求和響應遵循的緩存機制。在請求消息或響應消息中設置Cache-Control並不會修改另外一個消息處理過程當中的緩存處理過程。請求時的緩存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached,響應消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age。

Php代碼實現:header("cache-control: abc");abc是上述指令值一個或多個,多個用’,’分開

Expires:告訴瀏覽器指明應該在何時認爲文檔已通過期,從而再也不緩存它。代碼實現:header("Expires:". date('D, d M Y H:i:s \G\M\T', time()+10));--------這個是把時間截轉化成格林時區字符串給expires頭域,這個顯示時間會比中國北京時間少8個小時,東8區的實現:header("Expires:". date('r', time()+10))

last-modified:這個是服務器返回給瀏覽器,瀏覽器下次請求則把該值賦給if-modified-since頭域傳給服務器,服務器就能夠根據此值判斷是否有改變,有則繼續運行下去,否者返回304 not modified。Php設置expires頭域同樣。

代碼:

  1. <?php

  2. if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && (time()-strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) < 10)) {  


  3.  header("HTTP/1.1 304 Not Modified");  


  4.     exit;  


  5.   }  


  6. header("Last-Modified: " . date('D, d M Y H:i:s \G\M\T', time()) );或者header("Last-Modified: " . date('r', time()) );  

  7. ?>

前者是格林時間格式,後者是中國時間。須要注意的就是php.ini 的時區prc則用後則,否者前者。筆者曾經試過在時區是prc的狀況下用了前者,致使time()-strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) <0永遠成立,由於是負值。

注意:當請求頁面有session_start()的時候,則無論是否有expires、cache-control、last-modified設置,則返回給客戶端Cache-Control頭域爲Cache-Control:no-store, no-cache, must-revalidate Expires頭域 Expires:Thu, 19 Nov 1981 08:52:00 GMT。這個問題煩了筆者2天,都覺得php.ini 或是apache的問題。最後居然是session_start()的問題。

2、             瀏覽器緩存動態

前面介紹了http headers幾個告訴瀏覽器如何處理緩存。但不一樣瀏覽器處理各類頭域的方式不一樣,如下就是筆者。

Ps:各個瀏覽器監聽http headers的方法能夠查看:http://www.phpben.com/?post=76

(1)header(「cache-control: no-store」)

IE9

Google17.0

Firefox11

Maxthon3

點擊刷新鍵

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

地址欄回車

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

點擊後退鍵

同上

同上

同上

同上

(2)header(「cache-control: no-cache」)

IE9

Google17.0

Firefox11

Maxthon3

點擊刷新鍵

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

地址欄回車

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

點擊後退鍵

同上

From cache

From cache

同上

(3)header(「cache-control:bublic」)

IE9

Google17.0

Firefox11

Maxthon3

點擊刷新鍵

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

地址欄回車

from cache

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

點擊後退鍵

From cache

From cache

From cache

同上

(4)header("cache-control:private"); header("cache-control: must-revalidate ")

IE9

Google17.0

Firefox11

Maxthon3

點擊刷新鍵

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

地址欄回車

除第一次外都是from cache

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

點擊後退鍵

From cache

From cache

From cache

同上

(5)header("cache-control:max-age=num");num是秒數

IE9

Google17.0

Firefox11

Maxthon3

點擊刷新鍵

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

地址欄回車

秒數<num from cache

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

點擊後退鍵

From cache

From cache

From cache

同上

(6)header("Expires:". date('D, d M Y H:i:s \G\M\T', time()+num)); num是秒數

IE9

Google17.0

Firefox11

Maxthon3

點擊刷新鍵

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

地址欄回車

秒數<num from cache

重發請求,返回200狀態

重發請求,返回200狀態

重發請求,返回200狀態

點擊後退鍵

From cache

From cache

From cache

同上

(7)if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && (time()-strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) < num)) {

 header("HTTP/1.1 304 Not Modified");

    exit;

  } header("Last-Modified: " . date('D, d M Y H:i:s \G\M\T', time()) );

IE9

Google17.0

Firefox11

Maxthon3

點擊刷新鍵

秒數<num 304 not modified

秒數<num 304 not modified

秒數<num 304 not modified

重發請求,返回200狀態

地址欄回車

from cache

秒數<num 304 not modified

秒數<num 304 not modified

重發請求,返回200狀態

點擊後退鍵

From cache

From cache

From cache

同上

結論:

一、刷新對於任何瀏覽器且無論是什麼cache-control,都會從新請求,通常返回是200,除非Last-Modified設置

二、後退鍵除非no-cache; no-store外都是使用緩存

三、Cache-control:no-store 在瀏覽器中任何操做都從新提交請求,包括後退

四、遨遊3的緩存不好

五、IE9 的緩存很強,因此用ie9調試的時候儘量點刷新而不是在地址欄回車

鑑於這種狀況,對於不一樣的應用(有些要緩存,有些常常更新)對於不一樣的國家各類瀏覽器份額,而哪一種緩存方式。中國IE比較多,加上360瀏覽器的加入(用IE內核),那就要主要參照IE瀏覽器。

但筆者仍是比較喜歡header("Last-Modified: " . date('D, d M Y H:i:s \G\M\T', time()) );這種方式。結合起來connection:keep-alive能讓緩存技術更成熟。

注意

一、也許你會問,用Cache-control:no-store或Cache-control:no-store,但調試頁面仍是沒原來的緩存。而後清除瀏覽器緩存關掉重啓瀏覽器, 緩存還在。這是由於你的web應用用了文件緩存如ecshop常出現這種狀況,這種狀況就要進web應用後臺刪除文件緩存。

二、調試的時候儘量不要在地址欄回車,特別是IE,google還好一點,可是要知道此次的測試只是各個瀏覽器中的一個版本,因此調試的時候儘量點刷新按鈕。

三、但在cache-control:max-age=num 和expires 一塊兒使用的時候,前者級別比較高,瀏覽器會忽略expires的設置。(上面沒給出測試內容)

3、             Sessioncookies

Session 、cookies是程序員永遠討論的話題之一。

一、簡單說一下cookiessession

(1)Cookies是保存在客戶端的小段文本,隨客戶端點每個請求發送該url下的全部cookies到服務器端。好比在谷歌瀏覽器下,打開ww.abc.com下的兩個文件,a.php包含cookies1和cookies2,b.php包含了cookies3和cookies4,那麼在a.php或b.php 點任意一個鏈接(固然是ww.abc.com服務器上的),瀏覽器就會把cookies1~4這4個cookies發送給服務器。可是若是在IE9有打開一個包含cookies5的c.php,哪門在google瀏覽器點擊鏈接是不會發送cookies5的。

(2)Session則保存服務器段,經過惟一的值sessionID來區別每個用戶。SessionID隨每一個鏈接請求發送到服務器,服務器根據sessionID來識別客戶端,再經過session 的key獲取session值。SessionID傳回服務器的實現方式能夠經過cookies和url回寫來實現。

注意

一、同一個瀏覽器打開同一個文件,如a.php ,或同時有設置session的兩個文件a.php、b.php sessionID則只有一個。(時間上不能是打開a.php關閉瀏覽器再打開b.php)

二、不一樣瀏覽器在同一時間打開贊成文件的sessionID也不同

三、sessionID是服務器生成的不規則惟一字符串,如:

PHPSESSID=05dbfffd3453b7be02898fdca4fcd82b;------ PHPSESSID能夠經過php.ini中session.name來改變,因此筆者在監聽一些大型網站的時候查不出PHPSESSID,這是一個安全因素。

(3)cookies、session在php中的主要相關參數

(1)session.save_handler = 」files」

默認以文件方式存取session數據,若是想要使用自定義的處理器來存取session數據,好比數據庫,用」user」。

(2)session.use_cookies = 1 前面說到sessionID用cookies來實現,這裏就是,1表示用cookies

(3)session.use_trans_sid = 0 上面是用cookies來實現sessionID,這裏值如果1則使用url回寫方式,級別比session.use_cookies高

(4)session.use_only_cookies = 0 值爲1則sessionID只能夠用cookies實現,級別比前兩個高

(5)session.cache_expire =180  session 緩存過時的秒數

(6)session.gc_maxlifetime = 1440

設定保存的session文件生存期,超過此參數設定秒數後,保存的數據將被視爲’垃圾’並由垃圾回收程序清理。判斷標準是最後訪問數據的時間(對於FAT文件系統是最後刷新數據的時間)。若是多個腳本共享同一個session.save_path目錄但session.gc_maxlifetime不一樣,將以全部session.gc_maxlifetime指令中的最小值爲準。

(4)圖說cookie 、ssession

php代碼以下

  1. session_start();  


  2. $_SESSION['favcolor'] = 'green';  


  3. $_SESSION['animal']   = 'cat';  


  4. $_SESSION['time']     = time();  


  5. setcookie("cookie1","www.phpben.com",time()+3600*10);  


  6. setcookie("cookie2","www.phpben.com",time()+3600*10);  

圖片:

結論:

一、 第一次請求是沒用cookies的,而第二次有PHPSESSID和兩個cookies是由於服務器第一請求返回這個三個cookies。

二、第二次請求比第一次多返回PHPSESSID這個cookies,在第二次則沒有了,直到session過時後從新設置。

二、;瀏覽器關掉cookiessession是否能夠正常運行?

前面說起sessionID的時候有兩種方式。

(1)cookies 方式,在session.use_trans_sid=0 and session.use_cookies = 1的狀況下使用。這種方法是每次瀏覽器端點每一個請求,都把sessionID發送到服務器。

(2)url回寫,session.use_only_cookies = 0 and session.use_trans_sid=1的狀況下,服務器會忽略session.use_trans_sid,在瀏覽器發hhtp請求後,服務器會在返回頁面內容中每一個鏈接後面加上PHPSESSID=05dbfffd3453b7be02898fdca4fcd82b (在php.ini沒改session.name,默認是PHPSESSID),這樣就算客戶端的瀏覽器禁止了cookies,同樣能實現session功能。

這裏來個測試:

在1.php文件代碼:

  1. <?php  


  2. echo 'Welcome to page #1<br/>';  


  3. session_start();  


  4. $_SESSION['favcolor'] = 'green';  


  5. $_SESSION['animal']   = 'cat';  


  6. $_SESSION['time']     = time();  




  7. // Works if session cookie was accepted


  8. echo '<br /><a href="2.php">page 1 (這個href中沒SID參數)</a><br/>';  




  9. // Or maybe pass along the session id, if needed


  10. echo '<br /><a href="2.php?' . SID . '">page 2 (這個href中有SID參數)</a><br/>';  


  11. ?>  


在2.php文件代碼:

  1. <?php  


  2. session_start();  




  3. echo 'Welcome to page #2<br />';  




  4. echo $_SESSION['favcolor'],'<br/>'; // green


  5. echo $_SESSION['animal'],'<br/>';   // cat


  6. echo date('Y m d H:i:s', $_SESSION['time']),'<br/>';  




  7. // You may want to use SID here, like we did in page1.php


  8. echo '<br /><a href="1.php">return page 1</a>';  


  9. ?>  

情景1:沒禁用瀏覽器的cookies(用cookies實現session),則在2.php能正常輸出

情景2:禁用用瀏覽器的cookies且在php.ini開啓session.use_trans_sid=1,經過1.php第一鏈接過去顯示不了session的值,但第二個鏈接則正常顯示。(說明url回寫正常運行)

參考:http://blog.csdn.net/heiyeshuwu/article/details/7604538

寫一個簡單的爬蟲 - 如何模擬登陸網站 - 從波 - 博客園 - Google Chrome (2013/5/16 10:16:41)

寫一個簡單的爬蟲 - 如何模擬登陸網站

清除瀏覽器保存的 cookie

chrome 是這個地址:chrome://chrome/settings/cookies,搜索如163,而後remove all

firefox 是 Edit -> Preferences -> Privacy -> remove individual cookies

(或許還須要關閉已經登陸的網頁)

 

查看完整的登陸過程

chrome 的 Developer Tools 因爲會隨着 url 的跳轉發生改變,不能查看到完整的登陸過程

firefox 的 httpfox 插件能夠捕捉到完整的登陸過程

 

以163的登陸過程爲例:

chrome 捕捉到的依次爲:next.jsp, main.jsp, master.css等

 

firefox 捕捉到的依次爲:logins.jsp, next.jsp, main.jsp, master.css等

chrome 的 Developer Tools 漏掉了一個最關鍵的 login.jsp

所以模擬網站的登陸過程,以 firefox 爲主,完整捕捉登陸過程;用chrome 輔助查看分析,能夠很方便地看到總體的狀況,也很方便複製。

tips:若是看到Request Headers裏帶有Cookie,說明前面還有一步獲取Cookie的過程。

 

分析登陸過程

第一步的 https://reg.163.com/logins.jsp,post 的數據有三個,其中有username, password,都是明文的。

試試在 url 後面加上 post 的數據看看會發生什麼,https://reg.163.com/logins.jsp?username=abcdefg@163.com&password=1234567,將username, password 換爲本身的,登陸成功。

一、能夠用post方法帶上post的數據訪問url;

二、也能夠用get方法直接訪問加了post數據的url;

三、還能夠直接複製 chrome 的 Developer Tools 中最終跳轉的頁面的 headers,帶那個headers訪問該頁面,如:

 

登陸成功後,保存返回的cookie,後續操做帶着該cookie訪問便可。

HTTP代理如何正確處理Cookie【轉】_zboole_新浪博客 - Google Chrome (2013/5/6 14:38:10)

HTTP代理如何正確處理Cookie【轉】

  (2011-02-12 15:43:42)
標籤: 

雜談

分類: 協議分析
大多數的 Web 應用程序都要求維護某種會話狀態,如用戶購物車的內容。這種會話狀態的保持不少狀況下須要藉助於Cookie或者Session的幫助。本文結合在線頁面 翻譯 (Machine Translation System)項目中對於Cookie的處理方法,探討一下如何在HTTP應用代理中正確處理Cookie的傳遞和管理問題。

讀者定位爲具備Java和Web開發經驗的開發和設計人員。

讀者能夠學習到關於Cookie的工做原理和Cookie協議的細節,以及在一個HTTP應用代理的場景下Cookie的管理和處理思想,並能夠直接使用文中的代碼和思路,提升工做效率。

隨着愈來愈多的系統移植到了Web上,HTTP協議具備了比之前更普遍的應用。不一樣的系統對WEB實現提出了不一樣的要求,基於HTTP協議的網絡應 用正趨於複雜化和多元化。不少應用須要把用戶請求的頁面進行處理後再返回給用戶,好比頁面關鍵字過濾,頁面內容緩存、內容搜索、頁面翻譯等等。這些應用在 實際效果上相似於一個HTTP應用代理:它們首先接受用戶的請求,根據用戶請求的URL去真正的目標服務器取回目標頁面,再根據不一樣應用的要求作出相應處 理後返回給用戶。這樣用戶直接面對的就是這個HTTP應用代理,而經過它與其餘頁面進行交互。Cookie或Session技術的應用,解決了HTTP協 議的一個問題 -- 沒法保持客戶狀態,所以它如今被普遍應用於各類Web站點中。上面提到的那些應用若是不能處理好Cookie和Session的傳遞、更新和廢除等問題, 就會極大的限制它們所能處理站點的範圍,所以如何在HTTP應用代理中正確處理Cookie,成爲一個必須解決的問題。本文結合在頁面翻譯 (Machine Translation System)項目中對於Cookie的處理方法,探討一下這方面的解決方案。

MTS項目簡介及討論前提

Machine Translation System(如下簡稱MTS)是一個在線實時頁面翻譯系統,爲用戶在線提供把英文頁面翻譯成其餘9種語言的服務。用戶經過向MTS系統提交一個相似下面 的URL使用此服務,其中參數url指明瞭用戶所須要翻譯的目標地址,參數language指明瞭所需翻譯成的目標語言,www.mts.com是假想中 提供MTS服務的站點。

HTTP://www.mts.com/translate?url=http://www.ibm.com/&language=French

一個完整的MTS系統處理過程能夠分解成如下幾個步驟:

  • 用戶向MTS提交合適的URL。
  • MTS在接到用戶的請求後,解析出用戶須要翻譯的目標地址和目標語言,根據用戶請求的目標地址,把請求轉發到目標服務器。
  • MTS接受來自目標服務器的應答,包括頁面信息和HTTP頭信息。
  • MTS在肯定獲得正確的目標頁面後,把頁面內容送入WebSphere Translation Server進行翻譯。
  • 把翻譯後的頁面連同修改後的HTTP頭信息提交給用戶。

MTS邏輯圖(略)


固然,這其中涉及到不少的應用處理。好比與各類HTTP/HTTPS站點創建聯結、根據HTTP頭信息進行頁面跳轉和錯誤處理、爲始終保持用戶在翻 譯模式下而對目標的HTML頁面進行分析和修改,根據系統設置對某些DNT(Do Not Translate)的頁面進行過濾和跳轉,固然還有對Cookie的處理等等。其餘問題跟這篇文章關聯不大,咱們重點討論在這種狀況下的Cookie處 理。Cookie跟隨目標服務器的HTTP頭信息被MTS接收到,通過MTS整理以後發給客戶端瀏覽器。MTS在接到下一次用戶對同一個站點的翻譯請求 時,再把從客戶端獲得的Cookie發送給目標服務器。

在以上的場景中,MTS充當的做用相似於一種HTTP應用代理服務器,它代替用戶取得目標頁面,並在做出相應處理後再提交給用戶。固然,這種代理服 務器不須要用戶修改瀏覽器的代理服務器參數或者網絡配置,而只是簡單的在瀏覽器的地址欄中輸入一個MTS可以識別的URL便可。此篇文章也是在這樣一個應 用場景的基礎上,展開對HTTP應用代理服務器如何處理Cookie的討論。

問題的產生

在MTS系統中,目標服務器的Cookie在兩個地方會產生問題。當MTS接收目標服務器應答的時候,Cookie隨着HTTP頭信息被MTS接收 到的。這時候目標服務器認爲MTS就是最終客戶,所以它賦予了Cookie與目標服務器相符的屬性。而若是MTS把這些Cookie原封不動的保存在 HTTP頭信息中,傳給真正的最終用戶的話,用戶的瀏覽器會由於這些Cookie不合法而忽略它們。同理,當Cookie從瀏覽器端傳回目標服務器的時 候,也會遇到相同的問題。所以有必要對Cookie進行一些處理,以保證用戶的瀏覽器能真正識別和利用這些Cookie。

可是爲什麼用戶瀏覽器沒法識別從目標服務器傳過來的原始Cookie呢?這是由於出於安全性的考慮,Cookie規範制定的時候對Cookie的產生 和接受設置了一些嚴格的規範,不符合這些規範的Cookie,瀏覽器和服務器都將予以忽略。下面咱們從Cookie規範入手進行介紹。

Cookie的規範介紹

目前有如下幾種Cookie規範:

  • Netscape cookie草案:是最先的cookie規範,基於rfc2109。儘管這個規範與rc2109有較大的差異,可是不少服務器都與之兼容。
  • rfc2109, 是w3c發佈的第一個官方cookie規範。理論上講,全部的服務器在處理cookie(版本1)時,都要遵循此規範。遺憾的是,這個規範太嚴格了,以至不少服務器不正確的實施了該規範或仍在使用Netscape規範。
  • rfc2965規範定義了cookie版本2,並說明了cookie版本1的不足。

rfc2965規範的使用,目前並很少。rfc2109規範相應要嚴格得多,在實際應用上,並非全部的瀏覽器和Web服務器都嚴格遵照。所以相比 較而言,Netscape cookie草案卻是一個比較簡潔和被普遍支持的Cookie規範,所以咱們在這裏以Netscape cookie草案爲基礎進行討論,對於其餘兩種規範,咱們的討論和代碼具備相同的意義。關於Netscape cookie草案的細節,你們能夠參照Netscape官方站點,這裏咱們列舉一些和咱們討論有關的內容。

根據Netscape cookie草案的描述,Cookie 是Web 服務器向用戶的瀏覽器發送的一段ASCII碼文本。一旦收到Cookie,瀏覽器會把Cookie的信息片段以"名/值"對(name-value pairs)的形式儲存保存在本地。這之後,每當向同一個Web 服務器請求一個新的文檔時,Web 瀏覽器都會發送之站點之前存儲在本地的Cookie。建立Cookie的最初目的是想讓Web服務器可以經過多個HTTP請求追蹤客戶。有些複雜的網絡應 用須要在不一樣的網頁之間保持一致,它們須要這種會話狀態的保持能力。

瀏覽器與Web服務器經過HTTP協議進行通信,而Cookie就是保存在HTTP協議的請求或者應答頭部(在HTTP協議中,數據包括兩部分,一 部分是頭部,由一些名值對構成,用來描述要被傳輸數據的一些信息。一部分是主體(body),是真正的數據(如HTML頁面等))進行傳送的。

在HTML文檔被髮送以前,Web服務器經過傳送HTTP 包頭中的Set-Cookie 消息把一個cookie 發送到用戶的瀏覽器中。下面是一個遵循Netscape cookie草案的完整的Set-Cookie 頭:


Set-Cookie:customer=huangxp; path=/foo; domain=.ibm.com; 
expires= Wednesday, 19-OCT-05 23:12:40 GMT; [secure]

Set-Cookie的每一個屬性解釋以下:

  • Customer=huangxp 一個"名稱=值"對,把名稱customer設置爲值"huangxp",這個屬性在Cookie中必須有。
  • path=/foo 控制哪些訪問可以觸發cookie 的發送。若是沒有指定path,cookie 會在全部對此站點的HTTP 傳送時發送。若是path=/directory,只有訪問/directory 下面的網頁時,cookie才被髮送。在這個例子中,用戶在訪問目錄/foo下的內容時,瀏覽器將發送此cookie。若是指定了path,可是path 與當前訪問的url不符,則此cookie將被忽略。
  • domain=.ibm.com 指定cookie被髮送到哪臺計算機上。正常狀況下,cookie只被送回最初向用戶發送cookie 的計算機。在這個例子中,cookie 會被髮送到任何在.ibm.com域中的主機。若是domain 被設爲空,domain 就被設置爲和提供cookie 的Web 服務器相同。若是domain不爲空,而且它的值又和提供cookie的Web服務器域名不符,這個Cookie將被忽略。
  • expires= Wednesday, 19-OCT-05 23:12:40 GMT 指定cookie 失效的時間。若是沒有指定失效時間,這個cookie 就不會被寫入計算機的硬盤上,而且只持續到此次會話結束。
  • secure 若是secure 這個詞被做爲Set-Cookie 頭的一部分,那麼cookie 只能經過安全通道傳輸(目前即SSL通道)。不然,瀏覽器將忽略此Cookie。
 
 
Cookie有一個Expires(有效期)屬性,這個屬性決定了Cookie的保存時間,服務器能夠經過設定Expires字段的數值,來改變Cookie的保存時間。若是不設置該屬性,那麼Cookie只在瀏覽網頁期間有效,關閉 瀏覽器 ,這些Cookie自動消失,絕大多數網站屬於這種狀況。一般狀況下,Cookie包含Server、Expires、Name、value這幾個字段,其中對服務器有用的只是Name和value字段,Expires等字段的內容僅僅是爲了告訴瀏覽器如何處理這些Cookies。
 

一旦瀏覽器接收了cookie,這個cookie和對遠端Web服務器的連續請求將一塊兒被瀏覽器發送。例如 前一個cookie 被存入瀏覽器而且瀏覽器試圖請求 URL http://www.ibm.com/foo/index.html 時,下面的HTTP 包頭就被髮送到遠端的Web服務器。

GET /foo/index.html HTTP/1.0
Cookie:customer=huangxp


一次典型的網絡瀏覽過程

在瞭解了Cookie協議的一些基本內容以後,讓咱們看看一次典型的網絡瀏覽過程當中瀏覽器如何識別和處理Cookie:

  • 瀏覽器對於Web服務器應答包頭中Cookie的操做步驟:
    1. 從Web服務器的應答包頭中提取全部的cookie。
    2. 解析這些cookie的組成部分(名稱,值,路徑等等)。
    3. 斷定主機是否容許設置這些cookie。容許的話,則把這些Cookie存儲在本地。
  • 瀏覽器對Web服務器請求包頭中全部的Cookie進行篩選的步驟:
    1. 根據請求的URL和本地存儲cookie的屬性,判斷那些Cookie能被髮送給Web服務器。
    2. 對於多個cookie,斷定發送的順序。
    3. 把須要發送的Cookie加入到請求HTTP包頭中一塊兒發送。

由MTS代理的網絡瀏覽過程

以上咱們瞭解了在一個典型的瀏覽器與Web服務器交互的時候,Cookie的傳遞過程。下面咱們將看到,若是在MTS代理網絡瀏覽的過程當中,不對Cookie進行修改,上面的Cookie傳遞過程將沒法實現。

1. 假設用戶但願把 http://www.ibm.com/foo/index.html 頁面翻譯成法文,應該使用以下的url對MTS發出請求

http://www.mts.com/translate?url=http://www.ibm.com/foo/index.html&language=French

2. MTS接收用戶的請求,鏈接遠程目標服務器 http://www.ibm.com/foo/index.html。目標服務器作出應答,返回HTTP頭和HTML頁面內容。其中,典型的HTTP頭內容以下:


HTTP/1.1 200 OK
Date: Mon, 24 Oct 2005 06:54:41 GMT
Server: IBM_HTTP_Server
Cache-Control: no-cache
Content-Length: 19885
Connection: close
Set-Cookie:customer=huangxp; path=/foo; domain=.ibm.com; 
expires= Wednesday, 19-OCT-05 23:12:40 GMT
Content-Type: text/html

3. MTS不對Set-Cookie後的內容做任何處理,直接把它加到用戶瀏覽器的應答頭上發送給瀏覽器。

4. 瀏覽器將從Set-Cookie中解析出domain和path的值,分別是.ibm.com和/foo,並與請求的url:http: //www.mts.com/translate?url=http://www.ibm.com/foo/index.html&language =French進行比較。請求url的domain是www.mts.com,path是/,與Set-Cookie中的屬性不符,因此瀏覽器將忽略此 Cookie。

另外,在瀏覽器發送Cookie的時候也會遇到一樣的問題,一樣如上例,若是瀏覽器裏原本已經存儲了http: //www.ibm.com/foo/的Cookie,但因爲用戶要經過MTS訪問此站點,瀏覽器經不會把已經存儲的Cookie上轉到MTS中,MTS 也就沒法把之傳遞到http://ibm.com/foo/上。

基於上面Cookie規範的介紹和例證,咱們能看出,瀏覽器在接受某一個站點的Cookie的時候,須要檢查Cookie的參數domain、 path、secure,看是否與當前的站點和URL相符,若是不符的話,就會忽略。另外一方面。瀏覽器在上傳Cookie的時候,也會根據當前所訪問站點 的屬性,上傳相關的Cookie,而其餘的Cookie則不予上傳。

至此,咱們討論了須要修改Cookie的根本緣由在於Cookie規範的限制。下面咱們討論兩種解決問題的思路。

解決問題的兩種思路

Cookie的存在是要解決HTTP協議自己先天的缺陷-無狀態性,它爲用戶保存了一些須要的狀態信息。所以咱們解決此問題的最本質的出發點,也就是找到一種途徑能爲用戶保存Cookie所提供用戶狀態信息,實際上就是Name/Value對。

思路一

第一種思路就是修改目標服務器取得的Cookie,使之符合MTS站點的屬性,而後做爲MTS站點的Cookie存儲到用戶的瀏覽器中去。固然,這 種修改必須保留原始Cookie的全部屬性值,當之後訪問同一個目標服務器的時候,MTS能根據保存的屬性值還原出原始Cookie,而後進行提交。

具體到屬性值的保存位置,沒有太多選擇的餘地,實際上,domain,path,secure,expires這幾個屬性都沒法利用,只有利用 name=value這一屬性對。咱們的作法是創造一個新的Cookie,把原始Cookie的domain,path的值與name值進行編碼,用分隔 符附加在Name值的後面,符值給新的Cookie。這樣作也同時避免了不一樣目標服務器若是出現同名的Cookie,將會互相覆蓋的狀況(Cookie規 範裏面也規定了,客戶端以domain,path,name做爲Cookie的惟一標示)。而原始Cookie的secure和expires值,直接符 給新的Cookie,新Cookie的domain和path設成缺省值,這樣,新Cookie就能夠被瀏覽器正常接受。因爲瀏覽器接受的全部 Cookie的domain和path值都同樣,所以每次用戶對MTS提出請求時,瀏覽器都會把全部與MTS站點相關的Cookie上傳,所以,MTS還 須要還原原始的Cookie,過濾掉與目標服務器不相干的Cookie,而後上傳有用的Cookie。

這種思路的優勢在於Cookie存儲在客戶端,能夠作到長期存儲,瀏覽器本身根據Cookie的expires值作出判斷,省掉不少開發的麻煩。缺 點是轉換的過程相對較複雜。另外還有一個缺點,也是因爲Cookie規範的限制所形成的。Cookie規範對於一個瀏覽器同時可以存儲的Cookie數量 做出了規定。

  • 總共300 個cookie
  • 每一個Cookie 4 K 的存儲容量
  • 每個domain 或者 server 20 個cookie。

以上是瀏覽器所應達到的最小存儲數量,超出這個限制,瀏覽器應該自動按照最少最近被使用的原則刪除超出得Cookie。因爲用戶有可能經過MTS這 一個網站翻譯大量的目標服務器,所以瀏覽器存儲在MTS的domain下的cookie數量就頗有可能超過20個,這時候就會致使某些Cookie被刪 除。通常這也不會形成太大問題,由於規範是要求瀏覽器刪除最少最近被使用的Cookie,但咱們在實際測試當中發現有些瀏覽器並不遵照這樣的規範,而是刪 除最新的Cookie,這就將致使用戶很大的不便。

思路二

第二種思路在於把原始的Cookie組織成dataBean,存儲到用戶的Session當中去。這樣,在用戶端只須要存儲一個SessionID 的Cookie,而不須要存儲全部目標服務器的每個Cookie。另外,當接收到用戶的又一次翻譯請求時,再從Session當中取出全部的 dataBean,逐一進行分析,找出與用戶所請求的目標服務器相符的原始Cookie,進行提交。

這種思路能夠克服上一種思路中Cookie超過標準數量時的缺陷,並且不需編碼保存原始的Cookie屬性值,減小了程序的複雜度。缺點是須要程序 員本身處理expires。並且因爲是把Cookie存儲在Session中,一旦Session失效,全部Cookie都將被刪除,因此,沒法保存那些 長期的Cookie。

總之,兩種思路各有利弊,在實際應用當中要權衡考慮。下面咱們針對兩種思路進行技術實現,分別對應方案一和方案二。

因爲MTS須要與目標服務器鏈接,遵循HTTP協議讀取和返回Cookie,可是若是用JDK中的java.net.URLConnection處理Cookie將很是不方便,所以咱們使用HTTPClient來處理與目標服務器的鏈接。

方案一:Cookie存儲在瀏覽器端

用戶每發起一次新的請求,瀏覽器在檢查完本地存儲Cookie的有效性後,會把全部由MTS產生的有效Cookie附加在請求頭裏送到MTS。 MTS接受到客戶端的翻譯請求後,從Request中提取出全部的Cookie,還原後根據目標服務器的domain和path進行過濾。產生全部與目標 服務器相關的Cookie。


//從request中獲取全部的Cookie
javax.servlet.http.Cookie[] theCookies = request.getCookies();
ArrayList cookiesList = new ArrayList();
String url = request.getParameter("url");
String domain = URLUtil.getURLHost(url);
String path = URLUtil.getPath(url);
if (theCookies != null)
{
for (int i = 0; i < theCookies.length; i++)
{
RE r = new RE();
//用正則表達式把name項還原成domain,path,name
REDebugCompiler compiler = new REDebugCompiler();
r.setProgram(compiler.compile("\\|\\|"));
String[] values = r.split(theCookies[i].getName());
//"9.181.116.183||/MTModule||testCookie:value1" or " || 
||testCookie:value1"
if (values.length == 3)
{
if (values[0].trim().startsWith("."))
{
if (!domain.endsWith(values[0].trim()))
continue;
} else if (!domain.endsWith("://" + values[0].trim()))
continue;
if (!path.startsWith(values[1].trim()))
continue;
Cookie tempCookie = new Cookie();
tempCookie.setDomain(
("".equals(values[0].trim())) ? null : values[0]);
tempCookie.setPath(
("".equals(values[1].trim())) ? null : values[1]);
tempCookie.setName(
("".equals(values[2].trim())) ? null : values[2]);
tempCookie.setSecure(theCookies[i].getSecure());
tempCookie.setValue(theCookies[i].getValue());
tempCookie.setVersion(theCookies[i].getVersion());
tempCookie.setComment(theCookies[i].getComment());
cookiesList.add(tempCookie);
}
}
}
//transferedCookie用來存儲將被傳到目標服務器的Cookie
Cookie[] transferedCookie = new Cookie[cookiesList.size()];
cookiesList.toArray(transferedCookie);

接下來,須要把Cookie送到目標服務器中。咱們使用HTTPClient與目標服務器鏈接。HTTPClient在與目標服務器鏈接之後,容許 服務器設置Cookie並在須要的時候自動將Cookie返回服務器,也支持手工設置 Cookie後發送到服務器端。可是,因爲如何處理cookie有幾個規範互相沖突:Netscape Cookie 草案、RFC210九、RFC2965,並且還有很大數量的軟件商的Cookie實現不遵循任何規範。 爲了處理這種情況,須要把HttpClient設置成Cookie兼容模式,這樣能夠最大限度的處理好各類Cookie。下面的代碼把Cookie送到目 標服務器。


HttpClient client = new HttpClient();
//從request獲得全部須要傳輸的cookie 
Cookie[] questCookie = getCookieFromRequest(request);
//設置HTTPClient爲Cookie兼容模式
client.getState().setCookiePolicy(CookiePolicy.COMPATIBILITY);
if (questCookie.length > 0)
//把Cookie加到httpclient中
client.getState().addCookies(questCookie);
HttpMethod method = new GetMethod(TagerURL);
//向目標服務器發送請求
int statusCode = client.executeMethod(method);
method.releaseConnection();

MTS把請求和Cookie送出後,繼續接收目標服務器的應答,讀取返回的原始Cookie,並轉換成能夠存儲在用戶瀏覽器端的Cookie。下面 的代碼將對原始Cookie的內容進行變換,保留expires和secure等項,把domain和path項編碼到name中去。


//從HTTPClient中取得全部的Cookie
Cookie[] temp = client.getState().getCookies();
if (temp != null)
{
javax.servlet.httpCookie theCookie = new javax.servlet.http.Cookie[temp.length];
//逐一對Cookie進行處理
for (int i = 0; i < temp.length; i++)
{ StringBuffer sb = new StringBuffer();
//編碼成domain||path||name
sb.append(
temp[i].getDomain() == null ? " " : temp[i].getDomain());
sb.append("||");
sb.append(temp[i].getPath() == null ? " " : temp[i].getPath());
sb.append("||");
sb.append(temp[i].getName() == null ? " " : temp[i].getName());
theCookie[i] =
new Cookie(sb.toString(),temp[i].getValue());
//複製其餘項
theCookie[i].setMaxAge(theCookie[i].getMaxAge();
theCookie[i].setSecure(temp[i].getSecure());
theCookie[i].setVersion(temp[i].getVersion());
theCookie[i].setComment(temp[i].getComment());
}
}

最後一步,把這些Cookie保存到response裏,隨HTTP應答頭返回用戶瀏覽器。並保存在瀏覽器中。


//把全部轉換後的Cookie加入response
for (int i = 0; i < theCookie.length; i++) {
response.addCookie(theCookie[i]);
}

至此,咱們已經完成了接收用戶請求,轉換Cookie,發送到目標服務器,接收目標服務器的原始Cookie,並保存在客戶瀏覽器的整個處理過程。


方案二:Cookie存儲在服務器端

在此種方案中,目標服務器返回給MTS的Cookie將被組織成dataBean,存儲在用戶的Session中。所以,咱們首先生成一個用來存儲 Cookie的類CookiesBean,根據它的特性,它能夠繼承ArraryList類。此對象將存儲用戶訪問目標服務器時接收到的全部 Cookie,並提供與新接收到的Cookie融合的功能,同時可以刪除過時的Cookie,更新同名的Cookie。


public class CookiesBean extends ArrayList
{

public CookiesBean(Cookie[] cook)
{
if (cook == null)
return;
//add all cookie which isn't expired.
for (int i = 0; i < cook.length; i++)
{
if (!cook[i].isExpired())
{
add(cook[i]);
}
}
}

public void RefreshBean(CookiesBean bean)
{
if (bean == null)
return;
Iterator it = bean.iterator();
//針對bean中的每個Cookie進行處理
while (it.hasNext())
{
Cookie beanCookie = (Cookie) it.next();
if (beanCookie == null) continue;
ArrayList drop = new ArrayList();
Iterator thisIt = iterator();
//取出存儲的Cookie進行比較和處理
while (thisIt.hasNext())
{
Cookie thisCookie = (Cookie) thisIt.next();
if (thisCookie == null) continue;
//比較name,domain和path,若是同樣的話,則把此Cookie移到drop中
if (CommonMethods
.CompString(beanCookie.getName(), thisCookie.getName())
&& CommonMethods.CompString(
beanCookie.getDomain(),
thisCookie.getDomain())
&& CommonMethods.CompString(
beanCookie.getPath(),
thisCookie.getPath()))
{
drop.add(thisCookie);
continue;
}
//刪除過時的Cookie
if (thisCookie.isExpired())
drop.add(thisCookie);
}
//刪除全部drop中的Cookie
this.removeAll(drop);
//若是beanCookie有效,則加入到存儲區中。
if (!beanCookie.isExpired())
add(beanCookie);
}
return;
}
}

當MTS接受到客戶端的翻譯請求後,會從Session中提取出全部的dataBean,並獲得存儲的全部Cookie。如如下代碼:


CookiesBean dataBean = null;
Cookie[] theCookies = new Cookie[0];
ArrayList cookiesList = new ArrayList();
//得到Session,並得到dataBean
HttpSession session = request.getSession(false);
if (session != null)
{
dataBean = (CookiesBean) session.getAttribute(SESSION_NAME);
}
else
{
return theCookies;
}

MTS在全部的存儲的Cookie中,檢查Cookie的Domain、path和secure的值,篩選出符合目標服務器的Cookie。


//提取目標服務器的domain和path
String url = context.getURL();
String domain = URLUtil.getURLHost(url);
String path = url.substring(domain.length());

String cookiedomain = null;
String cookiepath = null;
//逐個比較Cookie的domain和path
//把符合要求的Cookie紀錄到cookiesList中
for (int i = 0; i < dataBean.size(); i++)
{
Cookie cookie = (Cookie) dataBean.get(i);
if (cookie == null) continue;
cookiedomain =
(cookie.getDomain() == null) ? "" : cookie.getDomain();
cookiepath = (cookie.getPath() == null) ? " " : cookie.getPath();
if (!path.startsWith(cookiepath))
continue;
if (cookiedomain.startsWith("."))
{
if (!domain.endsWith(cookiedomain))
continue;
}
else if (!domain.endsWith("://" + cookiedomain))
continue;
if (cookie.isExpired())
continue;
if (cookie.getSecure() && url.toLowerCase().startsWith("http:"))
continue;
cookiesList.add(cookie);
}
theCookies = new Cookie[cookiesList.size()];
cookiesList.toArray(theCookies);
return theCookies;

把Cookie送到目標服務器的代碼與方案一基本同樣,在此忽略。

最後一步,須要把Cookie存儲到Session中。下面的代碼將從目標服務器接受Cookie,融入到dataBean中,並保存到客戶的Session中。


//從目標服務器獲得Cookie集
Cookie[] cookies = client.getState().getCookies();
CookiesBean bean = new CookiesBean(cookies);
CookiesBean dataBean = bean;
//取得用戶Session
HttpSession session = request.getSession(false);
if (session != null)
{
if (session.getAttribute(SESSION_NAME) != null)
{
//讀取Session中存取的dataBean
dataBean = (CookiesBean) session.getAttribute(SESSION_NAME);
//目標服務器端的Cookie融合到Session中的dataBean中
dataBean.RefreshBean(bean);
}
//把最終的dataBean存入Session中
session.setAttribute(SESSION_NAME, dataBean);
}

至此,咱們已經完成了在Session中保存個目標服務器所產生Cookie的整個處理過程。

關於Session的考慮

在研究完如何管理和傳遞Cookie以後,咱們也須要研究一下Session的傳遞。由於目前大部分站點都在採用Session機制保存用戶狀態數據,若是不能解決Session的傳遞問題,HTTP應用代理服務器的適用範圍一樣會大打折扣。

首先咱們瞭解一下Session的實現機制。Session是一種服務器端的機制,服務器使用一種相似於散列表的結構來保存信息。當程序須要爲某個 客戶端的請求建立一個session的時候,服務器首先檢查這個客戶端的請求裏是否已包含了一個session標識 - 稱爲session id,若是已包含一個session id則說明之前已經爲此客戶端建立過session,服務器就按照session id把這個session檢索出來使用(若是檢索不到,可能會新建一個),session id的值應該是一個既不會重複,又不容易被找到規律以仿造的字符串。

保存這個session id的方式之一就是採用Cookie。通常這個Cookie的名字都相似於 SESSIONID。好比WebSphere對於Web應用程序生成的Cookie:JSESSIONID= 0001HWF4iVD94pY8Cpbx6U4CXkf:10lro0398,它的名字就是 JSESSIONID。

保存session id的其餘方式還包括URL重寫和表單隱藏字段。這兩種方式都不須要代理服務器做特殊處理。所以實際上,咱們解決了Cookie的管理和傳遞的問題以後,也就解決了Session的管理和傳遞。



結束語

從上面的討論中能夠看出,因爲Cookie自己的規範限制,HTTP應用代理所必需面對的一個問題就是如何對Cookie進行正確的處理。本文對此 提出了兩種解決思路並列出了實現代碼。對於MTS項目自己,咱們使用的是第二種方案。開發人員在認識好Cookie自己的特性以後,參照本文的思路,根據 本身系統的特色,也會找出更適宜的解決方案。

轉自:http://blog.csdn.net/joliny/archive/2008/11/23/3355239.aspx

原文(繁體):http://www.ibm.com/developerworks/tw/library/j-cookie/

 

參考資料

  • Netscape Cookie Specification 對Netscape Cookie 使用的特性進行了簡要的介紹。

  • RFC2965:HTTP State Management Mechanism 介紹了HTTP 狀態管理機制

  • RFC2109 w3c發佈的第一個官方cookie規範

  • RFC2616:Hypertext Transfer Protocol 超文字傳輸協定

  • Ronald Tschalr開發了HTTPClient,將其做爲URLConnection 的替代品。

  • Jakarta Regexp Apache 的開源專案,處理正則運算式的java組。

 

細說Cookie - Fish Li - 博客園 - Google Chrome (2013/4/18 11:06:27)

Cookie雖然是個很簡單的東西,但它又是WEB開發中一個很重要的客戶端數據來源,並且它能夠實現擴展性很好的會話狀態, 因此我認爲每一個WEB開發人員都有必要對它有個清晰的認識。本文將對Cookie這個話題作一個全面的描述, 也算是我對Cookie的認識總結。

Cookie 概述

Cookie是什麼? Cookie 是一小段文本信息,伴隨着用戶請求和頁面在 Web 服務器和瀏覽器之間傳遞。Cookie 包含每次用戶訪問站點時 Web 應用程序均可以讀取的信息。

爲何須要Cookie? 由於HTTP協議是無狀態的,對於一個瀏覽器發出的屢次請求,WEB服務器沒法區分 是否是來源於同一個瀏覽器。因此,須要額外的數據用於維護會話。 Cookie 正是這樣的一段隨HTTP請求一塊兒被傳遞的額外數據。

Cookie能作什麼? Cookie只是一段文本,因此它只能保存字符串。並且瀏覽器對它有大小限制以及 它會隨着每次請求被髮送到服務器,因此應該保證它不要太大。 Cookie的內容也是明文保存的,有些瀏覽器提供界面修改,因此, 不適合保存重要的或者涉及隱私的內容。

Cookie 的限制。 大多數瀏覽器支持最大爲 4096 字節的 Cookie。因爲這限制了 Cookie 的大小,最好用 Cookie 來存儲少許數據,或者存儲用戶 ID 之類的標識符。用戶 ID 隨後即可用於標識用戶,以及從數據庫或其餘數據源中讀取用戶信息。 瀏覽器還限制站點能夠在用戶計算機上存儲的 Cookie 的數量。大多數瀏覽器只容許每一個站點存儲 20 個 Cookie;若是試圖存儲更多 Cookie,則最舊的 Cookie 便會被丟棄。有些瀏覽器還會對它們將接受的來自全部站點的 Cookie 總數做出絕對限制,一般爲 300 個。

經過前面的內容,咱們瞭解到Cookie是用於維持服務端會話狀態的,一般由服務端寫入,在後續請求中,供服務端讀取。 下面本文將按這個過程看看Cookie是如何從服務端寫入,最後如何傳到服務端以及如何讀取的。

Cookie的寫、讀過程

在Asp.net中,讀寫Cookie是經過使用HttpCookie類來完成的,它的定義以下:

public sealed class HttpCookie
{
    // 獲取或設置將此 Cookie 與其關聯的域。默認值爲當前域。
    public string Domain { get; set; }
    // 獲取或設置此 Cookie 的過時日期和時間(在客戶端)。
    public DateTime Expires { get; set; }
    // 獲取一個值,經過該值指示 Cookie 是否具備子鍵。
    public bool HasKeys { get; }
    // 獲取或設置一個值,該值指定 Cookie 是否可經過客戶端腳本訪問。
    // 若是 Cookie 具備 HttpOnly 屬性且不能經過客戶端腳本訪問,則爲 true;不然爲 false。默認爲 false。
    public bool HttpOnly { get; set; }
    // 獲取或設置 Cookie 的名稱。
    public string Name { get; set; }
    // 獲取或設置要與當前 Cookie 一塊兒傳輸的虛擬路徑。默認值爲當前請求的路徑。
    public string Path { get; set; }
    // 獲取或設置一個值,該值指示是否使用安全套接字層 (SSL)(即僅經過 HTTPS)傳輸 Cookie。
    public bool Secure { get; set; }
    // 獲取或設置單個 Cookie 值。默認值爲空引用。
    public string Value { get; set; }
    // 獲取單個 Cookie 對象所包含的鍵值對的集合。
    public NameValueCollection Values { get; }
    // 獲取 System.Web.HttpCookie.Values 屬性的快捷方式。
    public string this[string key] { get; set; }
}

Cookie寫入瀏覽器的過程:咱們能夠使用以下代碼在Asp.net項目中寫一個Cookie 併發送到客戶端的瀏覽器(爲了簡單我沒有設置其它屬性)。

HttpCookie cookie = new HttpCookie("MyCookieName", "string value");
Response.Cookies.Add(cookie);

我想不少人都寫過相似的代碼,可是,你們有沒有想過:Cookie最後是如何發送到客戶端的呢?咱們打開Fiddler來看一下吧。

從上圖,您應該能發現,咱們在服務端寫的Cookie,最後實際上是經過HTTP的響應頭這種途徑發送到客戶端的。每個寫入動做, 都會產生一個【Set-Cookie】的響應頭。
瀏覽器正是在每次獲取請求的響應後,檢查這些頭來接收Cookie的。

Asp.net獲取Cookie的過程:咱們能夠使用以下代碼在Asp.net項目中讀取一個Cookie

HttpCookie cookie = Request.Cookies["MyCookieName"];
if( cookie != null )
    labCookie1.Text = cookie.Value;
else
    labCookie1.Text = "未定義";

代碼一樣也很簡單,仍是相似的問題:你們有沒有想過,Cookie是如何傳到服務端的呢?咱們仍是繼續使用Fiddler來尋找答案吧。

從圖片中,咱們能夠發現,Cookie是放在請求頭中,發送到服務端的。若是你一直刷新頁面,就能發現, 每次HTTP請求,Cookie都會被髮送。固然了,瀏覽器也不是發送它所接收到的全部Cookie,它會檢查當前要請求的域名以及目錄, 只要這二項目與Cookie對應的Domain和Path匹配,纔會發送。對於Domain則是按照尾部匹配的原則進行的。
因此,我在訪問 www.cnblogs.com 時,瀏覽器並不會將我在瀏覽 www.163.com 所接收到的 Cookie 發出去。

刪除Cookie:其實就是在寫Cookie時,設置Expires爲一個【早於如今時間的時間】。也就是:設置此Cookie已通過期, 瀏覽器接收到這個Cookie時,便會刪除它們。

HttpCookie cookie = new HttpCookie("MyCookieName", null);
cookie.Expires = new DateTime(1900, 1, 1);
Response.Cookies.Add(cookie);

使用Cookie保存複雜對象

前面的示例代碼大體演示了Cookie的讀寫操做。不過,咱們平時可能但願將更復雜的【自定義類型】經過Cookie來保存, 那麼又該如何操做呢?對於這個問題,咱們定義一個類型來看看如何處理。

public class DisplaySettings 
{
    public int Style;

    public int Size;
    
    public override string ToString()
    {
        return string.Format("Style = {0}, Size = {1}", this.Style, this.Size);
    }    
}

上面的代碼,我定義一個類型,用於保存用戶在瀏覽頁面時的顯示設置。接下來,我將介紹二種方法在Cookie中保存並讀取它們。

方法-1,經典作法。(注意前面給出的HttpCookie定義代碼中的最後二個成員)

private void WriteCookie_2a()
{
    DisplaySettings setting = new DisplaySettings { Style = 1, Size = 24 };

    HttpCookie cookie = new HttpCookie("DisplaySettings1");
    cookie["Style"] = setting.Style.ToString();
    cookie["Size"] = setting.Size.ToString();

    Response.Cookies.Add(cookie);
}

private void ReadCookie_2a()
{
    HttpCookie cookie = Request.Cookies["DisplaySettings1"];
    if( cookie == null )
        labDisplaySettings1.Text = "未定義";
    else {
        DisplaySettings setting = new DisplaySettings();
        setting.Style = cookie["Style"].TryToInt();
        setting.Size = cookie["Size"].TryToInt();
        labDisplaySettings1.Text = setting.ToString();
    }
}

方法-2,將對象JSON序列化爲字符串。

private void WriteCookie_2b()
{
    DisplaySettings setting = new DisplaySettings { Style = 2, Size = 48 };

    HttpCookie cookie = new HttpCookie("DisplaySettings2", setting.ToJson());
    Response.Cookies.Add(cookie);
}

private void ReadCookie_2b()
{
    HttpCookie cookie = Request.Cookies["DisplaySettings2"];
    if( cookie == null )
        labDisplaySettings2.Text = "未定義";
    else {
        DisplaySettings setting = cookie.Value.FromJson<DisplaySettings>();
        labDisplaySettings2.Text = setting.ToString();
    }
}

這段代碼使用了我定義的二個擴展方法。

對於這二種方法,我我的更喜歡後者,由於它具備更好擴展性:若是類型增長了成員,不須要修改讀寫Cookie的代碼。
不過,這種方式產生的有些字符,好比【雙引號】,極少數瀏覽器(Opera)不支持,因此須要作UrlEncode或者Base64編碼處理。
同理,對於第一種方法,遇到Value有【雙引號】時,咱們一樣須要作UrlEncode或者Base64編碼處理。

Js中讀寫Cookie

Cookie並不是只能在服務端讀寫,在客戶端的瀏覽器中也能夠實現對它的讀寫訪問。並且在JS中建立的Cookie對於服務端仍然有效(可見), 接下來咱們來看看在JS中如何寫入Cookie,演示代碼將建立一個按鈕,並在點擊按鈕後寫入Cookie

<input type="button" onclick="WriteCookie();" value="WriteCookie" />

<script type="text/javascript">
    function WriteCookie() {
        var cookie = "cookie_js=22222222; path=/";
        document.cookie = cookie;
    }    
</script>

在JS中寫Cookie很簡單,只要給document.cookie賦值一個Cookie字符串便可,至於格式,能夠參考前面用Fiddle看到的結果。

再來看一下如何使用JS讀取Cookie吧。請參考以下代碼:

<input type="button" onclick="ReadCookie();" value="ReadCookie" />

<script type="text/javascript">
    function ReadCookie() {
        alert(document.cookie);
    }    
</script>

仍然是訪問document.cookie,不過,此次咱們獲得倒是所有的Cookie值,每一個Key/Value項用分號分開,中間則用等號分開。 因此, 若是您想在JS中讀取Cookie,必定要按照這個規則來拆分並解析您要讀取的Cookie項。鑑於這樣的操做有些繁瑣, 咱們能夠jquery.cookie.js插件來輕鬆完成這個功能,有興趣的朋友也能夠看一下它是如何處理的。 這個插件的代碼比較少,這裏就直接貼出, 

注意哦:前面咱們看到了HttpCookie有個HttpOnly屬性,若是它爲true,那麼JS是讀不到那個Cookie的,也就是說: 咱們若是在服務端生成的Cookie不但願在JS中能被訪問,能夠在寫Cookie時,設置這個屬性。不過,經過一些工具,仍是能夠看到它們。

接下來,咱們再來看看Asp.net中Cookie有哪些應用。

Cookie在Session中的應用

在Asp.net中,HttpContext, Page對象都有個Session的對象,咱們能夠使用它來方便地在服務端保存一些與會話相關的信息。
前面咱們也提到過,HTTP協議是無狀態的,對於一個瀏覽器發出的屢次請求,WEB服務器沒法區分 是否是來源於同一個瀏覽器。 因此,爲了實現會話,服務端須要一個會話標識ID能保存到瀏覽器,讓它在後續的請求時都帶上這個會話標識ID,以便讓服務端知道 某個請求屬於哪一個會話,這樣即可以維護與會話相關的狀態數據。因爲Cookie對於用戶來講,是個不可見的東西,並且每次請求都會傳遞到 服務端,因此它就是很理想的會話標識ID的保存容器。在Asp.net中,默認也就是使用Cookie來保存這個ID的。注意:雖然Asp.net 2.0 也支持無Cookie的會話,但那種方式要修改URL,也有它的缺點,所以這種方法並無普遍的使用。本文將不對這個話題作過多的分析, 就此略過無Cookie會話這種方式。

咱們來看看Session是如何使用Cookie來保存會話標識ID的,在默認的Asp.net配置中,Web.config有着以下定義:

<sessionState mode="InProc" cookieName="ASP.NET_SessionId" cookieless="UseCookies"></sessionState>

若是咱們執行如下操做:

Session["Key1"] = DateTime.Now;

此時,咱們能夠使用一些瀏覽器提供的工具來查看一下如今的Cookie狀況。

從圖片上看,這個Cookie的名字就是咱們在配置文件中指出的名稱,咱們能夠修改一下配置文件:

<sessionState cookieName="SK"></sessionState>

再來執行上面的寫Session的操做,而後看Cookie

咱們能夠看到:SK的Cookie出現了。說明:在截圖時我把名稱爲"ASP.NET_SessionId"的Cookie刪除了。

經過上面示例,咱們能夠獲得結論,Session的實現是與Cookie有關的,服務端須要將會話標識ID保存到Cookie中。
這裏再一次申明,除非你使用無Cookie的會話模式,不然Session是須要Cookie的支持。反過來,Cookie並不須要Session的支持。

Cookie在身份驗證中的應用

我想不少人都在Asp.net的開發中使用過Form身份認證。對於一個用戶請求, 咱們能夠在服務端很方便地判斷它是否是表明一個已登陸用戶。

this.labStatus.Text = (Request.IsAuthenticated ? "已登陸" : "未登陸");

那麼,您有沒有好奇過:Asp.net是如何識別一個請求是否是一個已登陸用戶發起的呢?說到這裏,咱們就要從用戶登陸提及了。 爲了實現登陸及Form認證方式,咱們須要以下配置:

<authentication mode="Forms" >
    <forms name="UserStatus"></forms>
</authentication>

接下來,咱們須要實現用戶登陸邏輯。具體實現方式有不少,不過,最終的調用都是差很少的,以下代碼所示:

private void SetLogin()
{
    System.Web.Security.FormsAuthentication.SetAuthCookie("fish", false);
}

只要執行了以上代碼,咱們就能夠看到,前面的判斷【Request.IsAuthenticated】返回true,最終會顯示"已登陸"。 爲了探尋這個祕密,咱們仍是來看一下當前頁面的Cookie狀況。

果真,多出來一個Cookie,名稱與我在配置文件中指定的名稱相同。咱們再來看看若是註銷當前登陸會是什麼樣子的:

private void SetLogout()
{
    System.Web.Security.FormsAuthentication.SignOut();
}

看到了嗎,名爲"UserStatus"的Cookie不見了。此時若是你再去觀察【Request.IsAuthenticated】,能夠發現它此時返回 false。 或者,您也能夠再試一次,登陸後,直接刪除名爲"UserStatus"的Cookie,也能發現登陸狀態將顯示"未登陸"。 或許,您仍是有點不清楚前面我調用【System.Web.Security.FormsAuthentication.SetAuthCookie("fish", false);】後,Asp.net作了些什麼, 回答這個問題其實很簡單:本身用Reflector.exe去看一下Asp.net的實現吧。
這裏爲了更讓您能信服登陸與Cookie有關,我將直接建立一個Cookie看一下 Asp.net能不能承認我建立的Cookie,並認爲登陸有效。請看代碼:

private void SetLogin()
{
    //System.Web.Security.FormsAuthentication.SetAuthCookie("fish", false);

    // 下面的代碼和上面的代碼在做用上是等效的。
    FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
        2, "fish", DateTime.Now, DateTime.Now.AddDays(30d), false, string.Empty);
    string str = FormsAuthentication.Encrypt(ticket);

    HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, str);
    Response.Cookies.Add(cookie);
}

若是執行這段代碼,您將發現:【Request.IsAuthenticated】返回true,登陸狀態會顯示"已登陸"。
至此,咱們能夠得出一個結論: Form身份認證依賴Cookie,Asp.net就是每次檢查咱們在配置文件中指定的Cookie名稱,並解密這個Cookie來判斷當前請求用戶的登陸狀態。

Cookie的安全情況

從以上圖片,您應該能發現:瀏覽器能提供一些界面讓用戶清楚的觀察咱們在服務端寫的Cookie, 甚至有些瀏覽器還提供很方便的修改功能。以下圖所示:

因此,咱們在服務端寫代碼讀取Cookie時,尤爲是涉及類型轉換、反序列化或者解密時,必定要注意這些操做都有可能會失敗。 並且上圖也清楚的反映了一個事實:Cookie中的值都是「一目瞭然」的,任何人都能看到它們。因此,咱們儘可能不要直接在Cookie中 保存一些重要的或者敏感的內容。若是咱們確實須要使用Cookie保存一些重要的內容,但又不但願被他人看懂, 咱們能夠使用一些加密的方法來保護這些內容。

1. 對於一些重要性不高的內容,咱們能夠使用Base64之類的簡單處理方式來處理。

2. 對於重要性相對高一點的內容,咱們能夠利用.net提供的一些加密工具類,本身來設計加密方法來保護。不過, 密碼學與加密解密並非很簡單的算法,所以,本身設計的加密方式可能不會很安全。

3. 重要的內容,咱們能夠使用.net提供的FormsAuthenticationTicket,FormsAuthentication來加密。我認爲這種方式仍是比較安全的。 畢竟前面咱們也看過了,Asp.net的Form身份認證就是使用這種方式來加密用戶登陸的身份標識的,因此,若是這種方式不安全, 也就意味着Asp.net的身份認證也不安全了。 若是您使用這種方式來加密,那麼請注意:它產生的加密後文本仍是比較大的, 前面我也提到過,每次請求時,瀏覽器都會帶上與請求相匹配的全部Cookie,所以,這種Cookie會對傳輸性能產生必定的影響, 因此,請當心使用,切記不可過多的使用。

這裏要補充一下:去年曾經出現過【Padding Oracle Attack】這個話題, 一些人甚至錯誤的認爲是Asp.net加密方式不安全!若是您也是這樣認爲的,那麼能夠看一下這篇文章: 淺談此次ASP.NET的Padding Oracle Attack相關內容 ,以消除這個錯誤的認識。固然了,咱們也能夠從這個話題獲得一些收穫:解密失敗時,不要給出過多的提示,就當沒有這個Cookie存在。

如何在C#發請的請求中使用Cookie

前面咱們一直在談服務端與瀏覽器中使用Cookie,其實瀏覽器也是一個普通的應用程序,.net framework也提供一些類也能讓咱們 直接發起HTTP請求,下面咱們來看一下如何在C#發請的請求中使用Cookie ,其實也很簡單,主要是使用了CookieContainer類,請看如下演示代碼:

private static string SendHttpRequestGet(string url, Encoding encoding, 
            CookieContainer cookieContainer)
    {
        if( string.IsNullOrEmpty(url) )
            throw new ArgumentNullException("url");

        if( encoding == null )
            throw new ArgumentNullException("encoding");

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "GET";
        request.CookieContainer = cookieContainer;
        
        using( WebResponse response = request.GetResponse() ) {
            using( StreamReader reader = new StreamReader(response.GetResponseStream(), encoding) ) {
                return reader.ReadToEnd();
            }
        }
    }

    private void SendHttpDEMO()
    {
        StringBuilder sb = new StringBuilder();
        CookieContainer cookieContainer = new CookieContainer();

        string url = "http://www.taobao.com";
        SendHttpRequestGet(url, Encoding.Default, cookieContainer);

        // 後面能夠繼續發起HTTP請求,此時將會包含上次從服務器寫入的Cookie
        //SendHttpRequestGet("同域名下的其它URL", Encoding.Default, cookieContainer);

        // 至此,咱們能夠顯示取得了哪些Cookie
        CookieCollection cookies = cookieContainer.GetCookies(new Uri(url));
        if( cookies != null ) {
            foreach( System.Net.Cookie cookie in cookies )
                sb.AppendLine(cookie.ToString());
        }
        txtCookies.Text = sb.ToString();
    }

重構與使用總結

在前面的Asp.net示例代碼中,我一直使用.net提供的HttpCookie類來操做Cookie,是爲了展現用原始的方式來使用Cookie, 這些代碼有點重複,也有點繁瑣, 爲此,我提供了幾個簡單的方法能夠更容易的使用Cookie,也算是對Cookie使用的一個總結。

/// <summary>
/// 用於方便使用Cookie的擴展工具類
/// </summary>
public static class CookieExtension
{
    // 咱們能夠爲一些使用頻率高的類型寫專門的【讀取】方法

    /// <summary>
    /// 從一個Cookie中讀取字符串值。
    /// </summary>
    /// <param name="cookie"></param>
    /// <returns></returns>
    public static string GetString(this HttpCookie cookie)
    {
        if( cookie == null )
            return null;

        return cookie.Value;
    }

    /// <summary>
    /// 從一個Cookie中讀取 Int 值。
    /// </summary>
    /// <param name="cookie"></param>
    /// <param name="defaultVal"></param>
    /// <returns></returns>
    public static int ToInt(this HttpCookie cookie, int defaultVal)
    {
        if( cookie == null )
            return defaultVal;

        return cookie.Value.TryToInt(defaultVal);
    }

    /// <summary>
    /// 從一個Cookie中讀取值並轉成指定的類型
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="cookie"></param>
    /// <returns></returns>
    public static T ConverTo<T>(this HttpCookie cookie)
    {
        if( cookie == null )
            return default(T);

        return (T)Convert.ChangeType(cookie.Value, typeof(T));
    }

    /// <summary>
    /// 從一個Cookie中讀取【JSON字符串】值並反序列化成一個對象,用於讀取複雜對象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="cookie"></param>
    /// <returns></returns>
    public static T FromJson<T>(this HttpCookie cookie)
    {
        if( cookie == null )
            return default(T);

        return cookie.Value.FromJson<T>();
    }


    /// <summary>
    /// 將一個對象寫入到Cookie
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="name"></param>
    /// <param name="expries"></param>
    public static void WriteCookie(this object obj, string name, DateTime? expries)
    {
        if( obj == null )
            throw new ArgumentNullException("obj");

        if( string.IsNullOrEmpty(name) )
            throw new ArgumentNullException("name");
        

        HttpCookie cookie = new HttpCookie(name, obj.ToString());

        if( expries.HasValue )
            cookie.Expires = expries.Value;

        HttpContext.Current.Response.Cookies.Add(cookie);
    }

    /// <summary>
    /// 刪除指定的Cookie
    /// </summary>
    /// <param name="name"></param>
    public static void DeleteCookie(string name)
    {
        if( string.IsNullOrEmpty(name) )
            throw new ArgumentNullException("name");

        HttpCookie cookie = new HttpCookie(name);

        // 刪除Cookie,其實就是設置一個【過時的日期】
        cookie.Expires = new DateTime(1900, 1, 1);
        HttpContext.Current.Response.Cookies.Add(cookie);
    }
}

更完整的代碼能夠從本文的示例代碼中得到。(文章底部有下載地址)

使用方式:

public static class TestClass
{
    public static void Write()
    {
        string str = "中國";
        int aa = 25;
        DisplaySettings setting = new DisplaySettings { Style = 3, Size = 50 };
        DateTime dt = new DateTime(2012, 1, 1, 12, 0, 0);

        str.WriteCookie("Key1", DateTime.Now.AddDays(1d));
        aa.WriteCookie("Key2", null);
        setting.ToJson().WriteCookie("Key3", null);
        dt.WriteCookie("Key4", null);
    }

    public static void Read()
    {
        HttpRequest request = HttpContext.Current.Request;

        string str = request.Cookies["Key1"].GetString();
        int num = request.Cookies["Key2"].ToInt(0);
        DisplaySettings setting = request.Cookies["Key3"].FromJson<DisplaySettings>();
        DateTime dt = request.Cookies["Key4"].ConverTo<DateTime>();
    }    
}

注意哦:以上代碼中都是直接使用字符串"Key"的形式,這種方式對於大一些的程序在後期可能會影響維護。
因此建議:將訪問Cookie所使用的Key能有一個類來統一的定義,或者將讀寫操做包裝成一些屬性放在一個類中統一的管理。

public static class CookieValues
{
    // 建議把Cookie相關的參數放在一塊兒,提供 get / set 屬性(或者方法)來訪問,以免"key"處處亂寫

    public static string AAA
    {
        get { return HttpContext.Current.Request.Cookies["Key1"].GetString(); }
    }
    public static int BBB
    {
        get { return HttpContext.Current.Request.Cookies["Key2"].ToInt(0); }
    }
    public static DisplaySettings CCC
    {
        get { return HttpContext.Current.Request.Cookies["Key3"].FromJson<DisplaySettings>(); }
    }
    public static DateTime DDD
    {
        get { return HttpContext.Current.Request.Cookies["Key4"].ConverTo<DateTime>(); }
    }
}

補充

根據一些朋友提供的反饋,這裏再補充4個須要注意的地方:

1. 若是使用Form登陸驗證且但願使用Cookie方式時,建議設置 cookieless="UseCookies", 由於這個參數的默認值是:cookieless="UseDeviceProfile",Asp.net可能會誤判。 dudu就吃過虧。

<authentication mode="Forms" >
    <forms name="MyCookieName" cookieless="UseCookies"></forms>
</authentication>

2. Cookie有3個屬性,通常咱們能夠不用設置,但它們的值能夠在Web.config中指定默認值:

<httpCookies domain="www.123.com" httpOnlyCookies="true" requireSSL="false"/>

3. 雖然在寫Cookie時,咱們能夠設置name, value以外的其它屬性,可是在讀取時,是讀不到這些設置的。 其實在個人示例代碼中有體現,我前面也忘記了說明了。

4. HttpRequest.Cookies 與 HttpResponse.Cookies 會有關係(很奇怪吧)。
如下代碼演示了這個現象:

protected void Page_Load(object sender, EventArgs e)
{
    DateTime.Now.ToString().WriteCookie("t1", null);

    label1.Text = ShowAllCookies();

    Guid.NewGuid().ToString().WriteCookie("t2", null);

    // 若是去掉下面代碼,將會看到2個t1 
    Response.Cookies.Remove("t1");
    Response.Cookies.Remove("t2");
}

private string ShowAllCookies()
{
    StringBuilder sb = new StringBuilder();

    for( int i = 0; i < Request.Cookies.Count; i++ ) {
        HttpCookie cookie = Request.Cookies[i];
        sb.AppendFormat("{0}={1}<br />", cookie.Name, cookie.Value);
    }

    return sb.ToString();
}

上面的試驗代碼將會一直顯示 t1 的Cookie ,這裏就再也不貼圖了。

本文的全部示例代碼能夠點擊此處下載。

分類:   Asp.net
綠色通道:   好文要頂   關注我   收藏該文 與我聯繫  
« 博主上一篇: MongoDB實戰開發 【零基礎學習,附完整Asp.net示例】
» 博主下一篇: 細說 Form (表單)
posted on   2011-07-03 20:15   Fish Li  閱讀(32954) 評論( 147)   編輯   收藏
< Prev 1 2 3

#98樓   2011-09-22 10:49  |   小 剛Ⅰ    
若是存放sessionid的cookie不是在瀏覽器中,那麼爲何硬盤中不會產生session的相關cookie

瀏覽器每次從新打開都會產生一個新的sessionid, 關閉後若是存放在服務器中的session尚未過時,那麼下次訪問仍是有效的。不過sessionid已經變了

我的看法
#99樓 [ 樓主]   2011-09-22 12:18  |   Fish Li    
Cookie有臨時的和永久保存的。
#100樓   2011-10-27 12:17  |   Rainbow    
是篇好文章。用心寫得文章。
#101樓   2011-11-01 13:45  |   浪長街    
樓主回答粉絲的問題也很用心,不關注都不行
#102樓   2011-11-01 16:07  |   拉拉叟    
private static string SendHttpRequestGet(string url, Encoding encoding,  
CookieContainer cookieContainer)
{
if( string.IsNullOrEmpty(url) )
throw new ArgumentNullException("url");

if( encoding == null )
throw new ArgumentNullException("encoding");

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.CookieContainer = cookieContainer;

using( WebResponse response = request.GetResponse() ) {
using( StreamReader reader = new StreamReader(response.GetResponseStream(), encoding) ) {
return reader.ReadToEnd();
}
}
}

private void SendHttpDEMO()
{
StringBuilder sb = new StringBuilder();
CookieContainer cookieContainer = new CookieContainer();

string url = " http://www.taobao.com";
SendHttpRequestGet(url, Encoding.Default, cookieContainer);

// 後面能夠繼續發起HTTP請求,此時將會包含上次從服務器寫入的Cookie
//SendHttpRequestGet("同域名下的其它URL", Encoding.Default, cookieContainer);

// 至此,咱們能夠顯示取得了哪些Cookie
CookieCollection cookies = cookieContainer.GetCookies(new Uri(url));
if( cookies != null ) {
foreach( System.Net.Cookie cookie in cookies )
sb.AppendLine(cookie.ToString());
}
txtCookies.Text = sb.ToString();
}
///////////////////////////////////////////////////////
有個小地方 百思不得其解 望解答
這段代碼中 cookieContainer對象只是在request.CookieContainer = cookieContainer; 這行代碼中賦值給request.CookieContainer
爲何後來cookieContainer裏面有對象了呢?
#103樓 [ 樓主]   2011-11-01 17:37  |   Fish Li    
@拉拉叟
CookieContainer是個保存Cookie的容器,.net framework會在收到cookie後寫到裏面去的。
#104樓 [ 樓主]   2011-11-01 17:38  |   Fish Li    
引用浪長街:樓主回答粉絲的問題也很用心,不關注都不行

謝了。
#105樓   2011-11-01 17:39  |   拉拉叟    
謝謝樓主細心的解答
#106樓   2011-11-09 17:18  |   Domi.Z    
hi,fish:
請問你文章最後說HttpRequest.Cookies 與HttpResponse.Cookies 會有關係,實在有點摸不着頭腦,爲何會有這樣的結果呢?
#107樓 [ 樓主]   2011-11-09 18:43  |   Fish Li    
@Domi.Z
個人另外一篇博客【我心目中的Asp.net核心對象】有介紹。
#108樓   2011-11-28 15:46  |   丫頭騙子    
寫的很好,學習了。
#109樓   2011-12-21 08:57  |   小東1989    
寫的很好,使我加深對cookie的理解,學習中!!!
#110樓   2012-01-04 09:40  |   cwg2010    
樓主的排版簡潔,內容也不錯!必須頂!
#111樓 [ 樓主]   2012-01-04 20:12  |   Fish Li    
@cwg2010
多謝
#112樓   2012-01-14 21:15  |   xu_happy_you    
很好,很強大!收藏了!
#113樓   2012-01-16 16:44  |   dreamhappy    
一個公共方法的類庫,每一個方法都有註釋 (標準的///形式),編譯成dll後,在其餘項目中引用這個類庫,可是方法都沒有註釋,怎麼樣才能看到註釋呢?
#114樓 [ 樓主]   2012-01-16 22:38  |   Fish Li    
@dreamhappy
你要生成包含註釋的XML文件的。
項目 -> 屬性 -> 生成選項卡中,選擇「XML文檔文件」
#115樓   2012-01-30 15:20  |   刺客之家    
今天發現了一個特色,就是隻有當請求的頁面使用了Session對象時,asp.Net才爲該請求返回一個SetCookie的Response頭信息.
#116樓   2012-02-01 11:40  |   Domi.Z    
hi, fish。我問一下,能夠不能夠把相互依賴的字段 (例如城市ID和國家ID) 放入Cookie? 我目前只把城市ID放到Cookie中避免潛在攻擊,但這又給系統增長了複雜性(每一個用到國家ID的地方都要先經過城市ID去查找)。目前個人確尚未遇到過有用戶篡改Cookie,請問你有沒有遇到過相似的需求,你是如何處理的?
#117樓 [ 樓主]   2012-02-01 12:19  |   Fish Li    
@Domi.Z
很差意思,不太明白你的業務需求。
使用Cookie時只要記住:它只合適保存較小的數據,且對安全性的要求應該不高。在這個前提條件下,你放什麼數據,理論上都是能夠的。
#118樓   2012-03-13 10:38  |   Franky    
引用Cookie 的限制。 大多數瀏覽器支持最大爲 4096 字節的 Cookie。因爲這限制了 Cookie 的大小,最好用 Cookie 來存儲少許數據,或者存儲用戶 ID 之類的標識符。用戶 ID 隨後即可用於標識用戶,以及從數據庫或其餘數據源中讀取用戶信息。 瀏覽器還限制站點能夠在用戶計算機上存儲的 Cookie 的數量。大多數瀏覽器只容許每一個站點存儲 20 個 Cookie;...


這段話錯誤不少啊.

1. 大多數瀏覽器最大支持4096.
IE彷佛是4095 , Opera是4096, FF Safari 則是4097.

2. 大多數瀏覽器只容許每一個站點存儲 20 個 Cookie.
首先,站點,應該換位域, 而後就是20個的限制. 在2007 年的時候,微軟對IE6/IE7 發了一個補丁(KB937143 MS07-045),把cookie數目的上限從20提升到了50 (見 http://support.microsoft.com/kb/941495  )。 你可讓IE6/IE7的用戶安裝這個補丁來解決這個問題。 而IE8默認就是50 個. 至於其餘瀏覽器 FF,Opera,也都是50個. Safari比較特殊. 彷佛沒有限制. Chrome不知是否演習,沒有詳測.

3. 當cooke超量時,會丟棄最先的cookie,這個策略是ie,Opera的,firefox則不是.





posted @ 2017-01-16 16:53  suntl 閱讀( ...) 評論( ...) 編輯 收藏
相關文章
相關標籤/搜索