隨著Web應(yīng)用程序出現(xiàn)逞泄,產(chǎn)生了能夠直接在C端存儲用戶信息能力的要求铛漓。想法也合乎邏輯,屬于某個(gè)特定用戶的信息應(yīng)該存在該用戶的機(jī)器上士复。無論是登陸信息图谷、偏好設(shè)定或其他數(shù)據(jù)翩活。
這個(gè)問題的第一個(gè)方案是以cookie的形式出現(xiàn)的阱洪,cookie原來是網(wǎng)景公司創(chuàng)造的。
使用 Web Storage API
Cookie
HTTP Cookie菠镇,通常直接叫做cookie冗荸,最初是在用戶端用于存儲會話信息的。該標(biāo)準(zhǔn)要求服務(wù)器對任意HTTP請求發(fā)送Set-Cookie HTTP頭作為響應(yīng)的一部分利耍。其中包含會話信息蚌本。例如,這種服務(wù)器響應(yīng)的頭可能如下:
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value
Other-header: other-header-value
這個(gè)HTTP響應(yīng)設(shè)置以name為名稱隘梨、以value為值的一個(gè)cookie程癌,鍵值在傳送時(shí)都必須是URL編碼的。瀏覽器會存儲這樣的會話信息轴猎,并在這之后嵌莉,通過為每個(gè)請求添加Cookie HTTP頭將信息發(fā)送服務(wù)器,如下:
GET /index.html HTTP/1.1
Cookie: name=value
Other-header: other-header-value
發(fā)送回服務(wù)器的額外信息可以用于驗(yàn)證客戶來自于發(fā)送的哪個(gè)請求捻脖。
Cookie的限制
cookie在性質(zhì)上時(shí)綁定在特定的域名下的锐峭。當(dāng)設(shè)定了一個(gè)cookie后,再給創(chuàng)建它的域名發(fā)送請求時(shí)可婶,都會包含這個(gè)cookie沿癞。這個(gè)限制確保了存儲在cookie中的信息只能讓批準(zhǔn)的接受者訪問,而無法被其他域訪問矛渴。
關(guān)鍵詞:綁定在特定的域下
由于cookie時(shí)存在客戶端計(jì)算機(jī)上的椎扬,還加入了一些限制確保cookie不會被惡意使用,同時(shí)不會占據(jù)太多磁盤空間具温。每個(gè)域的cookie總數(shù)是有限的蚕涤。
- IE6以及更低版本限制每個(gè)域名20個(gè)cookie。
- IE7每個(gè)域50個(gè)桂躏。
- Firefox限制每個(gè)域50個(gè)钻趋。
- Opera限制每個(gè)域最多30個(gè)。
- Safari和Chrome對于每個(gè)域的cookie數(shù)量沒有限制(剂习?蛮位?较沪?)
當(dāng)超過上限后繼續(xù)設(shè)置cookie,瀏覽器就會清除以前設(shè)置的cookie失仁。
瀏覽器對cookie的大小也有限制尸曼,大多數(shù)瀏覽器都有大約4096B(+-1)的長度限制。為了最佳的瀏覽器兼容萄焦,最好將整個(gè)cookie長度限制在4095B以內(nèi)控轿。
如果嘗試創(chuàng)建超過最大尺寸限制的cookie,那么該cookie會悄無聲息的地丟掉拂封。
cookie的構(gòu)成
-
名稱
一個(gè)唯一確定cookie的名稱茬射。cookie名稱是不區(qū)分大小寫的,所以myCookie和MyCookie被認(rèn)為是同一個(gè)cookie冒签。然而在抛,實(shí)踐中最好區(qū)分大小寫。 -
值
存儲在cookie中的字符串值萧恕,值必須被URL編碼刚梭。 -
域
cookie對于哪個(gè)域是有效的,所有向該域發(fā)送的請求中都會包含這個(gè)cookie信息票唆。這個(gè)值可以包含子域朴读,也可以不包含,如果不做規(guī)定走趋,那個(gè)這個(gè)域會被認(rèn)作來自設(shè)置cookie的那個(gè)域衅金。 -
路徑
對于指定域中的那個(gè)路徑,應(yīng)該向服務(wù)器發(fā)送cookie吆视。 -
失效時(shí)間
表示cookie何時(shí)應(yīng)該被刪除的時(shí)間戳(也就是何時(shí)應(yīng)該停止向服務(wù)器發(fā)送這個(gè)cookie)典挑。默認(rèn)情況下瀏覽器會話結(jié)束時(shí)即將所有cookie刪除;不過也可以自己設(shè)置刪除時(shí)間啦吧。這個(gè)值是個(gè)GMT格式日期您觉。因此cookie可在瀏覽器關(guān)閉后依然存在用戶機(jī)器上。如果你設(shè)置的失效日期是個(gè)以前的時(shí)間授滓,則cookie會被立刻刪除琳水。 -
安全標(biāo)志
制定后,cookie只有在使用SSL連接的時(shí)候才發(fā)送到服務(wù)器般堆。例如在孝,cookie信息只能發(fā)送給https://www.wrox.com
,而http://www.wrox.com
的請求則不能發(fā)送cookie淮摔。
每一段信息都作為Set-Cookie
頭的一部分私沮,使用分號加空格分隔每一段。
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value; expires=Mon, 22-Jan-07 07:10:24 GMT; domain=.wrox.com
Other-header: other-header-value
頭信息指定了一個(gè)名為name的cookie和橙,它會在格林威治時(shí)間2007年1月22日7:10:24失效仔燕,同時(shí)對于www.wrox.com和wrox.com的任何子域(如p2p.wrox.com)都有效造垛。
secure標(biāo)志是cookie中唯一一個(gè)非鍵值對的部分,直接包含一個(gè)secure單詞晰搀。如下:
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value; domain=.wrox.com; path=/; secure
Other-header: other-header-value
這里設(shè)置了secure
標(biāo)志五辽,這個(gè)cookie只能通過SSL連接才能傳輸。
尤其要注意外恕,域杆逗、路徑、失效時(shí)間和secure標(biāo)志都是服務(wù)器給瀏覽器的指示鳞疲,已指定何時(shí)應(yīng)該發(fā)送cookoie罪郊。這些參數(shù)并不會作為發(fā)送服務(wù)器的cookie信息的一部分,只有鍵值對才會被發(fā)送建丧。
JS中的cookie
在JS中處理cookie有些復(fù)雜排龄,因?yàn)槠渲兴苤孽磕_的接口,即BOM的document.coookie
屬性翎朱。這個(gè)屬性的獨(dú)特指出在于 它會因?yàn)槭褂盟姆绞讲煌憩F(xiàn)出不同的行為。
當(dāng)用來獲取屬性值時(shí)尺铣,document.cookie返回當(dāng)前頁面可用的所有cookie的字符串拴曲,一系列由分號隔開的鍵值對。
name1=value1;name2=value2;name3=value3
所有名字和值都是URL編碼凛忿,所以必須使用docodeURLComponent()
解碼澈灼。
當(dāng)用于設(shè)置值的時(shí)候,document.cookie屬性可以設(shè)置為一個(gè)新的字符串店溢,這個(gè)cookie字符串會被解釋并添加到現(xiàn)有cookie集合中叁熔,設(shè)置document.cookie并不會覆蓋cookie,除非設(shè)置的cookie已經(jīng)存在床牧。設(shè)置格式如下:
name=value; expires=expiration_time; path=domain_path; domain=domain_name; secure
這些參數(shù)中荣回,只有cookie的名字和值是必需的。
document.cookie = "name=Nicholas";
這段代碼創(chuàng)建了一個(gè)名為name的cookie戈咳,值為Nicholas心软。當(dāng)客戶端每次向服務(wù)器發(fā)送請求的時(shí)候,都會發(fā)送這個(gè)cookie著蛙;當(dāng)瀏覽器關(guān)閉的時(shí)候删铃,他就會被刪除。
由于JS中讀寫cookie不是很直觀踏堡,常常需要一些函數(shù)簡化cookie功能猎唁。基本的cookie操作有三種:讀取顷蟆、寫入和刪除诫隅。他們在CookieUtil對象中如下:
var CookieUtil = {
get: function(name) {
var cookieName = encodeURIComponent(name) + "=",
cookieStart = document.cookie.indexOf(cookieName),
cookieValue = null;
if (cookieStart > 1) {
var cookieEnd = document.cookie.indexOf(";",cookieStart);
if (cookieEnd == -1) {
cookieEnd = document.cookie.length;
}
cookieValue = decodeURIComponent(document.coookie.substring(cookieStart + cookieName.length, cookieEnd));
}
return cookieValue;
},
set: function(name,value,expires,path,domain,secure) {
var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);
if (expires instanceof Date) {
cookieText += "; expires=" + expires.toGMTString();
}
if (path) {
cookieText += "; path=" + path;
}
if (domain) {
cookieText += "; domain=" + domain;
}
if (secure) {
cookieText += "; secure";
}
document.cookie = cookieText;
},
unset: function(name,path,domain,secure) {
this.set(name, "", new Date(0), path, domain, secure);
}
};
CookieUtil.get()方法根據(jù)cookie的名字獲取相應(yīng)的值缎患。它會在document.cookie字符串中查找cookie名加上等于號的位置。找到了使用indexOf()查找該位置后的第一個(gè)分號(表示該cookoie結(jié)束位置)阎肝。如果沒有分號挤渔,表示該cookie時(shí)字符串最后一個(gè),則余下的字符串都是cookie的值风题。該值使用decodeURLComponent()進(jìn)行解碼并最后返回判导。如果沒有發(fā)現(xiàn)cookie,則返回null沛硅。
......
可以這樣使用上述方法:
// 設(shè)置cookie
CookieUtil.set("name","Nicholas");
CookieUtil.set("book","Professional JavaScript");
// 讀取cookie值
console.log(CookieUtil.get("name")); // "Nicholas"
console.log(CookieUtil.get("book")); // "Professional JavaScript"
// delete cookie
CookieUtil.unset("name");
CookieUtil.unset("book");
// 設(shè)置cookie,包括它的路徑眼刃、域、失效日期
CookieUtil.set("name","Nicholas","/books/projs/","www.wrox.com",new Date("January 1, 2010"));
// 刪除剛剛設(shè)置的cookie
CookieUtil.unset("name","/books/projs/","www.wrox.com");
// 設(shè)置安全的cookie
CookieUtil.set("name","Nicholas",null,null,null,true);
Web Storage
Web Storage最早是在WHAT-WG的Web應(yīng)用1.0規(guī)范描述的摇肌。目的是客服由cookie帶來的一些限制擂红,當(dāng)數(shù)據(jù)需要被嚴(yán)格控制在客戶端時(shí),無須持續(xù)的將數(shù)據(jù)發(fā)回服務(wù)器围小。
- 提供一種在cookie之外存儲會話數(shù)據(jù)的途徑昵骤;
- 提供一種存儲大量可以跨會話存在的數(shù)據(jù)的機(jī)制。
最初的Web Storage規(guī)范包含了兩種對象定義:sesionStorage和globalStorage肯适。
Stroage類型
Storage類型提供最大的存儲空間(因?yàn)g覽器而異)來存儲鍵值對变秦。
clear()
刪除所有值;Firefox中沒有實(shí)現(xiàn)框舔。getItem(name)
根據(jù)指定的名字name獲取對應(yīng)的值蹦玫。key(index)
獲取index位置處的值的名字removeItem(name)
刪除由name指定的鍵值對。setItem(name,value)
為指定的name設(shè)置一個(gè)對應(yīng)的值刘绣。
Storage類型只能存儲字符串樱溉,非字符串的數(shù)據(jù)在存儲之前會被轉(zhuǎn)換成字符串
sessionStorage對象
sessionStorage對象存儲特定于某個(gè)會話的數(shù)據(jù),也就是該數(shù)據(jù)只保持到瀏覽器關(guān)閉纬凤。這個(gè)對象就像cookie福贞,也會在瀏覽器關(guān)閉后消失。sessionStorage中的數(shù)據(jù)可以跨越頁面刷新而存在移斩,同時(shí)瀏覽器支持肚医,瀏覽器崩潰后重啟依然可用。
因?yàn)閟essionStorage中的數(shù)據(jù)綁定于某個(gè)服務(wù)器會話向瓷,當(dāng)文件在本地運(yùn)行時(shí)不可用肠套。數(shù)據(jù)只能由最初給對象存儲數(shù)據(jù)的頁面訪問到。
//使用方法存儲數(shù)據(jù)
sessionStorage.setItem("name", "Nicholas");
// 使用屬性存儲數(shù)據(jù)
sessionStorage.book = "Professional JavaScript";
sessionStorage中有數(shù)據(jù)時(shí)猖任,可以使用getItem()或者通過直接訪問屬性名來獲取數(shù)據(jù)你稚。
// 使用方法讀取數(shù)據(jù)
var name = sessionStorage.getItem("name");
// 使用屬性讀取數(shù)據(jù)
var book = sessionStorage.book;
還可以通過結(jié)合length屬性和key()方法來迭代值。
for (var i = 0, len = sessionStorage.length; i < len; i++) {
var key = sessionStorage.key(i);
var value = sessionStorage.getItem(key);
console.log(key + "=" + value);
}
sessionStorage對象應(yīng)該主要用于僅針對會話的小段數(shù)據(jù)的存儲。如果需要跨越會話存儲數(shù)據(jù)刁赖,那么globalStorage
或者localStorage
更合適搁痛。
localStorage對象
要訪問同一個(gè)localStorage對象,頁面必須來自用一個(gè)域名宇弛,使用同一種協(xié)議鸡典,在同一個(gè)端口上。
由于localStorage時(shí)Storage的實(shí)例枪芒,所以可以像使用sessionStorage一樣使用它彻况。
localStorage.setItem("name","Nicholas");
localStorage.book = "Professional JavaScript";
var name localStorage.getItem("name");
var book = localStorage.book;
存儲在localStorage中的數(shù)據(jù)和存儲在globalStorage中的數(shù)據(jù)一樣,都遵循同樣規(guī)則舅踪,數(shù)據(jù)保留到通過JS刪除或者用戶清除瀏覽器緩存纽甘。
storage事件
對Storage對象做任何修改,都會在文檔上觸發(fā)storage事件抽碌,當(dāng)通過屬性或setItem()方法保存數(shù)據(jù)悍赢,使用delete操作符或removeItem()刪除數(shù)據(jù),調(diào)用clear()方法時(shí)货徙,都會發(fā)生該事件左权。這個(gè)事件event對象有以下屬性。
- domain:發(fā)生變化的存儲空間的域名
- key:設(shè)置或者刪除的鍵名
- newValue:如果設(shè)置新值破婆,則是新值涮总;如果刪除,則是null祷舀。
- oldValue:鍵被更改之前的值。
限制
與其他客戶端數(shù)據(jù)存儲方案類似烹笔,web storage同樣也有限制裳扯。大多數(shù)桌面瀏覽器會設(shè)置每個(gè)來源5MB的限制,chrome和safari對每個(gè)來源限制2.5MB谤职。而IOS版safari和Android版webkit的限制也是2.5MB饰豺。
對sessionStorage的限制也是因?yàn)g覽器而異。有的瀏覽器對sessionStorage的大小沒有限制允蜈。有關(guān)限制參考
參考: