数据存储
随着web应用的出现,也产生了对于能够直接在客户端上存储用户信息能力的要求。属于某个特定用户的信息应该存在该用户的机器上,无论是登录信息,偏好设定或其他数据,Web应用提供者发现他们在找各种方式将数据存在客户端上。
Cookie
cookie是服务器发送到浏览器并保存在浏览器上的一块数据,它会在浏览器下一次发送请求时被携带并发送到服务器上。
cookie主要用在以下三个方面:
- 会话状态管理
- 个性化设置
- 浏览器行为跟踪
当服务器收到HTTP请求时,可以在响应头里面增加一个Set-Cookie头部
比如,这种服务器响应头可能包含下面这种cookie信息:
Set-Cookie:name=value
这个HTTP响应设置以name为名称,以value为值的一个cookie,名称和值在传送时都必须是URL编码的。
浏览器会存储这样的会话信息,并在这之后,为每个请求添加Cookie HTTP头将信息发送回服务器:
Cookie:name=value
发送回服务器的额外信息可以用于唯一验证客户来自于发送的哪个请求。
限制
cookie在性质上是绑定在特定的域名下的。
当设定了一个cookie之后,再给创建它的域名发送请求时,都会包含这个cookie。
这个限制确保了储存在cookie中的信息只能让批准的接受者访问,而无法被其他域访问。
由于cookie是存在客户端计算机上的,还加入了一些限制确保cookie不会被恶意使用,同时不会占据太多的磁盘空间。每个域的cookie总数是有限的。浏览器之间的设定不同:
- IE7及以后:最多50个
- firefox:最多50个
- Opera:最多30个
- safari、chrome:没有硬性限制
当超过单个域名限制之后还要再设置cookie,浏览器就会清除以前设置的cookie。
浏览器中对于cookie的尺寸也有限制。大多数浏览器都有大约4096B的长度限制,限制了一个域下的所有cookie。
cookie的构成
cookie由浏览器保存的一下几块信息构成:
名称:一个唯一确定的cookie名称。cookie名称是不区分大小写的。
cookie的名称必须是经过URL编码的。值:储存在cookie中的字符串值。值必须被URL编码。
domain:指定cookie将要被发送到哪个域中。
如果没有明确设定,那么这个域会被认作来自设置cookie的那个域。
像 Yahoo! 这种大型网站,都会有许多 xxx.yahoo.com 形式的站点(例如:my.yahoo.com, finance.yahoo.com 等等)。将一个 cookie 的 domain 选项设置为 yahoo.com,就可以将该 cookie 的值发送至所有这些站点。浏览器会把 domain 的值与请求的域名做一个尾部比较(即从字符串的尾部开始比较),并将匹配的 cookie 发送至服务器。path:path 选项指定了请求的资源 URL 中必须存在指定的路径时,才会发送Cookie 消息头。
这个比较通常是将 path 选项的值与请求的 URL 从头开始逐字符比较完成的。如果字符匹配,则发送 Cookie 消息头
例如,指定cookie只有从http://www.wrox.com/books/中才能访问,那么http://www.wrox.com的页面就不会向服务器发送cookie信息。
需要注意的是,只有在 domain 选项核实完毕之后才会对 path 属性进行比较expires:表示cookie何时应该被删除的时间戳。
- 默认情况下,浏览器会话结束时即删除所有cookie。不过也可以自己设置删除时间。
- 这个值是一个GMT格式的日期,用于指定应该删除的cookie的准确时间。因此,cookie在浏览器关闭后依然可以保存在用户的机器上。
- 如果你设置的失效日期是个以前的时间,cookie会被立即删除。
Secure:指定后,cookie只有在使用SSL连接的时候才发送到服务器。
Http-only:加上此标志的cookie不能使用js通过document.cookie来访问,从而能够在一定程度上阻止XSS。当你不需要在JavaScript代码中访问你的Cookie时,可以将该Cookie设置成HttpOnly类型。
以上的每一段信息都作为Set-Cookie头的一部分,使用分号加空格分隔每一段:
Set-Cookie:name=value; expires=Mon, 22-Jan-07 07:10:24 GMT; domain=.wrox.com
该头信息指定了一个叫做name的cookie,它会在格林威治时间2007年1月22日7:10:24失效,同时对于www.wrox.com和wrox.com的任何子域都有效。
secure是cookie中唯一一个非名值对儿的部分,直接包含一个secure单词:
Set-Cookie:name=value; domain=.wrox.com; path=/; secure
这里建立了一个对所有wrox.com的子域和域名下(由path参数指定)所有页面都有效的cookie。因为设置了secure标志,这个cookie只能通过SSL连接才能传输。
尤其要注意,域、路径、失效时间和secure标志都是服务器给浏览器的指示,以指定何时应该发送cookie。但这些参数并不会做为发送到服务器的cookie信息的一部分,只有name value才会被发送。
JavaScript中的Cookie
BOM提供了document.cookie属性。
- 当用于获取属性值时,document.cookie返回当前页面可用的所有cookie字符串,一系列由分号隔开的名值对儿:
name1=value1;name2=value2;name3=value3
所有名字和值都是经过URL编码的,所以必须使用decodeURLComponent()来解码。
- 当用于设置值的时候,document.cookie属性可以设置为一个新的cookie字符串。这个cookie字符串会被解释并添加到现有的cookie集合中。
document.cookie = "name=oeschger";
document.cookie = "favorite_food=tripe";
alert(document.cookie);
// 显示: name=oeschger;favorite_food=tripe
设置document.cookie并不会覆盖cookie,除非设置的cookie的名称已经存在。
设置cookie的格式和Set-Cookie头中使用的格式一样:
name=value; expires=expiration_time; path=domain_path; domain=domain_name; secure
这些参数中,只有cookie的名字和值是必需的。
因为js自己处理cookie的接口并不是十分直观,因此一般都使用一些封装好的操作cookie的接口。
子cookie
为了绕开浏览器单域名下的cookie数限制,一些开发人员使用了一种称为子cookie的概念。子cookie是存放在单个cookie中的,用于存储多个名称值对儿。
子cookie一般以查询字符串的格式进行格式化,这些值可以使用单个cookie进行存储和访问,而非每个名称-值对儿使用不同的cookie存储。
有关cookie的思考
由于所有的cookie都会由浏览器作为请求头发送,所以在cookie中存储大量信息会影响到特定域的请求性能。cookie信息越大,完成对服务器请求的时间也就越长。尽管浏览器对cookie进行了大小限制,不过最好还是尽可能在cookie中少存储信息,以避免影响性能。
cookie的性质和它的局限性使得1其并不能作为存储大量信息的理想手段。
Web存储机制
Web Storage在HTML5中得到了实现。Web Storage的目的是克服由cookie带来的一些限制,当数据需要被严格控制在客户端上时,无需持续地将数据发回服务器。
Web Storage的两个主要目的是:
- 提供一种在cookie之外存储会话数据的途径
- 提供一种存储大量可以跨会话存在的数据的机制。
最初的web Storage规范包含了两种对象的定义:sessionStorage、globalStorage。这两个对象在支持的浏览器中都是以windows对象属性的形式存在的。
storage类型
Storage类型提供最大的存储空间来存储名值对儿。
Storage的实例与其他对象类似,有如下方法:
- clear():删除所有值
- getItem(name):根据指定的名字name获取对应的值
- key(Index):获得index位置处的值的名字
- removeItem(name):删除由name指定的名值对儿
- setItem(name,value):为指定的name设置一个对应的值
其中,getItem()、removeItem()和setItem()方法可以直接调用,也可以通过Storage对象间接调用。因为每个项目都是作为属性存储在该对象上的,所以可以通过点语法或者方括号语法访问属性来读取值,设置也一样,或者通过delete操作符进行删除。
但是还是建议使用方法而不是属性来访问数据,以免某个键会意外重写该对象上已经存在的成员。
还可以使用length属性来判断有多少名值对儿存放在Storage对象中。但无法判断对象中所有数据的大小。
Storage类型只能存储字符串,非字符串的数据在存储之前会被转换成字符串。
sessionStorage对象
sessionStorage对象存储特定于某个对话的数据,也就是该数据只保持到浏览器关闭。这个对象就像会话cookie,也会在浏览器关闭后消失。
存储在sessionStorage中的数据可以跨越页面刷新而存在,同时如果浏览器支持,浏览器崩溃并重启之后依然可用。
因为sessionStorage对象绑定于某个服务器会话,所以当文件在本地运行的时候是不可用的。
存储在sessionStorage中的数据只能由最初给对象存储数据的页面访问到,所以对多页面应用有限制。
由于sessionStorage对象其实是Storage的一个实例,所以可以使用setItem()或者直接设置新的属性来存储数据:
//使用方法存储数据
sessionStorage.setItem("name","nic");
//使用属性存储数据
sessionStorage.book = "js";
sessionStorage中有数据时,可以使用getItem()或者直接访问属性名来获取数据:
//使用方法读取数据
sessionStorage.getItem("name");
//使用属性读取数据
sessionStorage.book ;
还可以通过结合length属性和key()方法来迭代sessionStorage中的值:
for(var i=0, len=sessionStorage.length; i<len;i++){
var key = sessionStorage.key(i);
var value = sessionStorage.getItem(key);
alert(key+"="+value);
}
还可以使用for-in循环来迭代sessionStorage中的值:
for(var key in sessionStorage){
var value = sessionStorage.getItem(key);
alert(key+"="+value);
}
sessionStorage对象应该主要用于针对会话的小段数据的存储。如果需要跨越会话存储数据,那么globalStorage或者localStorage更为合适。
globalStorage对象
globalStorage对象的目的是跨越会话存储数据,但有特定的访问限制。
要使用globalStorage,首先要指定哪些域可以访问该数据。可以通过方括号标记使用属性来实现:
//保存数据
globelStorage["wrox.com"].name = "nicholas";
//获取数据
var name = globalStorage["wrox.com"].name;
在这里,访问的是针对域名wrox.com的存储空间。globalStorage不是Storage的实例,具体的globalStorage[“www.wrox.com”]才是。这个存储空间对于wrox.com及其所有子域都是可以访问的。
也可以像下面这样指定子域名:
//保存数据
globelStorage["www.wrox.com"].name = "nicholas";
//获取数据
var name = globalStorage["www.wrox.com"].name;
这里所指定的存储空间只能由来自www.wrox.com的页面访问,其他子域名都不行。
某些浏览器允许更加宽泛的访问限制,比如只根据顶级域名进行限制或允许全局访问:
//保存数据,任何人都可以访问
globelStorage[""].name = "nicholas";
//获取数据,可以任何以.net结尾的域名访问
var name = globalStorage["net"].name;
尽量避免使用这种可宽泛访问的数据存储,以防出现潜在的安全问题。因此使用globalStorage时一定要指定一个域名。
对globalStorage空间的访问,是依据发起请求的页面的域名、协议和端口来限制的。
例如,如果使用HTTPS协议在wrox.com中存储了数据,那么通过HTTP访问的wrox.com的页面就不能访问该数据。
globalStorage的每个属性都是Storage的实例,因此可以像下面这样使用:
globelStorage["www.wrox.com"].name = "nicholas";
globelStorage["www.wrox.com"].getItem("name");
globelStorage["www.wrox.com"].removeItem("name");
如果事先不能确定域名,那么使用location.host作为属性名比较安全:
globelStorage[location.host].name = "nicholas";
globelStorage[location.host].getItem("name");
如果不使用removeItem()或者delete删除,或者用户未清除浏览器缓存,存储在globalStorage属性中的数据就会一直保留在磁盘上。这让globalStorage非常适合在客户端存储文档或长期保存用户偏好设置。
localStorage对象
localStorage对象在修订过的HTML5中作为持久保存客户端数据的方案取代了globalStorage。
与globalStorage不同,不能给localStorage制定任何访问规则,规则事先就设定好了:
要访问同一个localStorage对象,页面必须来自同一域名(子域名无效),使用同一种协议,在同一个端口上。这相当于globalStorage[location.host]。
由于localStorage是Storage的实例,所以可以像使用sessionStorage一样来使用它:
localStorage.setItem("name","nicholas");
localStorage.book = "js";
var name = localStorage.getItem("name");
var book = localStorage.book;
存储在localStorage中的数据和存储在globalStorage中的数据一样,都遵循相同的规则:数据保留到通过js删除或者用户清除浏览器缓存。
为了兼容只支持globalStorage的浏览器,可以使用下面这个函数:
function getLocalStorage(){
if(typeof localStorage == "object"){
return localStorage;
}else if(typeof globalStorage == "object"){
return globalStorage[location.host];
}else{
alert("storage not available")
}
}
然后调用一次这个函数就可以正常读写数据了:
var storage = getLocalStorage();
storage对象
对Storage对象进行任何修改,都会在文档上触发storage事件。
当通过属性或setItem( )方发保存数据、使用delete操作符或removeItem()移除数据、或者调用clear()方法时,都会发生该事件。
这个时间的event对象有以下属性:
- domain:发生变化的存储空间的域名
- key:设置或删除的键名
- newValue:如果是设置值,则是新值;如果是删除键,则为null
- oldValue:键被更改之前的值
不是每个属性都被浏览器实现了。
无论对sessionStorage、globalStorage还是localStorage进行操作,都会触发storage事件,但不做区分。
限制
与其他客户端存储方案类似,Web Storage同样也有限制。这些限制因浏览器而异。
一般来说,对存储空间大小的限制都是以每个来源(协议、域和端口)为单位的。换句话说,每个来源都有固定大小的空间用于保存自己的数据。
对于localStorage而言,大多数浏览器会设置每个来源5MB的限制。
对sessionStorage的限制也因浏览器而异,有不限制的,有2.5MB、5MB的。