Cookie
HTTP Cookie,通常直接叫做cookie,最初是在客户端用于存储会话信息的。该标准要求服务器对任意HTTP请求发送Set-Cookie HTTP头作为响应的一部分,其中包含会话信息。
限制:
①Cookie无法跨域,只能是相同域、相同协议。
②每个域的Cookie总数是有限的,确保Cookie不会被而已使用,同时不会占据太多磁盘空间。当超过单个域名限制之后还要再设置cookie,浏览器就会清除以前设置的cookie。
③浏览器中对于cookie的尺寸也有限制。大多数浏览器都有大约4096B(+-1)的长度限制;如果超过那么该cookie就会被悄无声息地丢掉
注意点:
①cookie的名称是不分大小写的。然而实践中最好是将cookie名称看做是区分大小写的,因为某些服务器会这样处理cookie
②cookie的值和名称在传送时必须是URL编码的
③域:cookie对于哪个域是有效的。所有向该域发送的请求中都会包含这个cookie信息。如果没有明确设定,那么这个域会被认作来自设置cookie的那个域。
④路径:对于指定域中的那个路径,应该向服务器发送cookie。
⑤失效时间:表示cookie何时应该被删除的时间戳(也就是,何时应该停止向服务器发送这个cookie)。默认情况下,浏览器会话结束时即将 所有cookie删除;不过也可以自己设置删除时间。这个值是GMT格式的日期(Wdy, DD-Mon-YYYY HH:MM:SS GMT);如果你设置的失效日期是个以前的日期,则cookie会被立刻删除。
⑥安全标准:指定后,cookie只有在使用SSL连接的时候才发送到服务器。用secure来标志
JavaScript中的cookie
背景:在JavaScript中处理cookie有些复杂,因此其众所周知的蹩脚的接口,即BOM的document.cookie。这个属性的独特之处在于它会因为使用它的方式不同而表现出不同的行为。document.cookie返回当前可用的(根据cookie的域、路径、失效日期和安全设置)所有的cookie字符串,一系列由分号隔开的名值对儿(name1=value1&name2=value2)。
注意:
①所有名字和值都是经过URL编码的,所以必须使用decodeURLComponent()来解码。
②添加新的cookie值,设置document.cookie并不会覆盖cookie,除非设置的cookie的名称已经存在。因为这个cookie字符串会被解释并添加到现有的coookie集合中。
封装成JS库如下:
var CookieUtil = {
get: function (name){
var cookieName = encodeURIComponent(name) + "=",
cookieStart = document.cookie.indexOf(cookieName),
cookieValue = null,
cookieEnd;
if (cookieStart > -1){
cookieEnd = document.cookie.indexOf(";", cookieStart);
if (cookieEnd == -1){
cookieEnd = document.cookie.length;
}
cookieValue = decodeURIComponent(document.cookie.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); //没有删除已有cookie的直接方法,只能将失效时间设置为过去的时间
}
};
子cookie
为了绕开浏览器的单域名下的cookie,一些开发人员使用了一种成为子cookie(subcookie)的概念。子cookie是存放在单个cookie中的更小段的数据。也就是用用cookie值来存储多个名称值对儿。子cookie最常见的格式如:name=name1=value1&name2=value2。记得格式化子cookie。
var SubCookieUtil = {
get: function (name, subName){
var subCookies = this.getAll(name);
if (subCookies){
return subCookies[subName];
} else {
return null;
}
},
getAll: function(name){
var cookieName = encodeURIComponent(name) + "=",
cookieStart = document.cookie.indexOf(cookieName),
cookieValue = null,
cookieEnd,
subCookies,
i,
parts,
result = {};
if (cookieStart > -1){
cookieEnd = document.cookie.indexOf(";", cookieStart)
if (cookieEnd == -1){
cookieEnd = document.cookie.length;
}
cookieValue = document.cookie.substring(cookieStart + cookieName.length, cookieEnd);
if (cookieValue.length > 0){
subCookies = cookieValue.split("&");
for (i=0, len=subCookies.length; i < len; i++){
parts = subCookies[i].split("=");
result[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
}
return result;
}
}
return null;
},
set: function (name, subName, value, expires, path, domain, secure) {
var subcookies = this.getAll(name) || {};
subcookies[subName] = value;
this.setAll(name, subcookies, expires, path, domain, secure);
},
setAll: function(name, subcookies, expires, path, domain, secure){
var cookieText = encodeURIComponent(name) + "=",
subcookieParts = new Array(),
subName;
for (subName in subcookies){
if (subName.length > 0 && subcookies.hasOwnProperty(subName)){
subcookieParts.push(encodeURIComponent(subName) + "=" + encodeURIComponent(subcookies[subName]));
}
}
if (subcookieParts.length > 0){
cookieText += subcookieParts.join("&");
if (expires instanceof Date) {
cookieText += "; expires=" + expires.toGMTString();
}
if (path) {
cookieText += "; path=" + path;
}
if (domain) {
cookieText += "; domain=" + domain;
}
if (secure) {
cookieText += "; secure";
}
} else {
cookieText += "; expires=" + (new Date(0)).toGMTString();
}
document.cookie = cookieText;
},
unset: function (name, subName, path, domain, secure){
var subcookies = this.getAll(name);
if (subcookies){
delete subcookies[subName];
this.setAll(name, subcookies, null, path, domain, secure);
}
},
unsetAll: function(name, path, domain, secure){
this.setAll(name, null, new Date(0), path, domain, secure);
}
};
关于的cookie思考
由于所有的cookie都有由浏览器作为请求头发送,所以在cookie中存储大量信息会影响到特定域的请求性能。cookie信息越大,完成对服务器请求的时间也就越长。尽管浏览器对cookie进行了大小限制,不过最好还是尽可能在cookie中少存储信息,以避免影响性能。
注意:一定不要在cookie中存储重要和敏感的数据。cookie数据并非存储在一个安全的环境中,其中包含的任何数据都可以被他人访问。所以不要在cookie中存储注入信用卡号或者个人地址之类的数据。
Web存储机制
背景:Web Storage的目的是克服由cookie带来的一些限制,当数据需要被严格控制在客户端上时,无须持续地将数据发回服务器。Web Storage的两个主要目标是:
a.提供一种在cookie之外存储会话数据的途径
b.提供一种存储大量可以跨回话存在的数据的机制
Web Storage包含了两种对象的定义:sessionStorage和globalStorage。这两个对象在支持的浏览器中都是以window对象属性的形式存在的,支持这两个属性的浏览器包含IE8+、Firefox 3,。5+、Chrome 4+和Opera 10.5+。
Storage类型类型只能存储字符串。非字符串的数据在存储之前会被转换成字符串
1.Storage类型
Storage类型提供最大的存储空间来存储名值对儿
方法:
getItem(name): 根据指定的名字name获取对应的值
key(index): 获得index位置处的值的名字
removeItem(name): 删除由name指定的名值对儿
setItem(name, value):为指定的name设置一个对应的值
因为每个项目都是作为属性存储在该对象上的,所以可以通过点语法或者方括号语法访问属性来读取值,设置也一样,或者通过delete操作符进行删除。不过我们还建议读者使用方法而不是属性来访问数据,以免某个键会意外重写该对象上已经存在的成员。
①sessionStorage对象
sessionstorage对象存储于某个会话的数据,也就是该数据只保持到浏览器关闭。这个对象就像会话cookie,也会在浏览器关闭后消失。存储在sessionStorage中的数据可以跨越页面而存在,同时如果浏览器支持,浏览器崩溃并重启之后依然可用(Firefox和Webkit都支持,IE则不行)。
因为sessionStorage对象绑定于某个服务器会话,所以当文件在本地运行的时候是不可用的。存储在sessionStorage中的数据只能最初由对象存储数据的页面访问到,所以对多页面应用运用有限制。
不同浏览器写入数据方面略由不同。Firefox和Webkit实现了同步写入,所以添加到存储空间上的数据是立刻被提交的。IE的实现是异步写入数据,所以再设置和将数据实际写入磁盘之间可能有一定延迟。对于少量数据而言,这个差异可以忽略。对于大量数据,你会发现IE要比其他浏览器更快地恢复执行,因为它会跳过实际的磁盘写入过程。(就是数据库的事务知识,begin、commit).
注意点:delete操作符在Webkit中无法删除数据,removeItem()则可以在各种浏览器中正确运行。
sessionStorage对象应该主要仅针对会话的小段数据的存储。如果需要跨越会话存储数据,那么globalStorage或者localStorage更为合适。
EventUtil.addHandler(window, "load", function(){
document.getElementById("name-value").innerHTML = sessionStorage.getItem("name");
document.getElementById("book-value").innerHTML = sessionStorage.getItem("book");
EventUtil.addHandler(document.getElementById("delete-btn"), "click", function(){
//these don't work in webkit
//delete sessionStorage.name;
//delete sessionStorage.book;
sessionStorage.removeItem("name");
sessionStorage.removeItem("book");
});
EventUtil.addHandler(document.getElementById("see-btn"), "click", function(){
var i, key, value;
for (i=0, len = sessionStorage.length; i < len; i++){
key = sessionStorage.key(i);
value = sessionStorage.getItem(key);
alert(key + "=" + value);
}
});
//set some data
sessionStorage.setItem("name", "Nicholas");
sessionStorage.setItem("book", "Professional JavaScript");
});
②globalStorage对象
要使用globalStorage,首先要指定哪些域可以访问该数据。可以通过方括号使用属性来实现
globalStorage["wrox.com"].name = "Nocholas";
var name = globalStorage["wrox"].name;
在这里,访问的是针对域名wrox.com的存储空间。globalStorage对象不是Storage的实例,而具体的globalStorage["worx.com"]才是。
也可以指定子域名,那么只有指定来自子域名的页面才可以访问,其他子域名都不行。
对globalStorage空间的访问,是根据发起请求的页面的域名、协议和端口来限制的。这类似于Ajax请求的同源策略。
如果你实现不能确定域名,那么使用location.host作为属性名比较安全,获取的是一个主机名。
EventUtil.addHandler(window, "load", function(){
var dataStore = globalStorage[location.host];
document.getElementById("name-value").innerHTML = dataStore.getItem("name");
document.getElementById("book-value").innerHTML = dataStore.getItem("book");
EventUtil.addHandler(document.getElementById("delete-btn"), "click", function(){
//these don't work in webkit
//delete dataStore.name;
//delete dataStore.book;
dataStore.removeItem("name");
dataStore.removeItem("book");
});
EventUtil.addHandler(document.getElementById("see-btn"), "click", function(){
for (var i=0, len = dataStore.length; i < len; i++){
var key = dataStore.key(i);
var value = dataStore.getItem(key);
alert(key + "=" + value);
}
});
//set some data
dataStore.setItem("name", "Nicholas");
dataStore.setItem("book", "Professional JavaScript");
});
注意点:如果不使用removeItem()或者delete删除,或者用户未清除浏览器缓存,存储在globalStorage属性中的数据会一直保留在磁盘上。这让globalStoage非常适合在客户端存储文档或者长期保存用户偏好设置。
③localStorage对象
背景:localStorage对象在修改过的HTML5规范中作为持久保存客户端数据的访问取代了globalStorage。与globalStorage不同,不能给localStorage指定任何访问规则;规则事先就预订好了。要访问一个localStorage对象,页面必须来自同一个域名(子域名无效),使用同一种协议,在同一个端口上。这相当于事先设置了globalStorage[location.host]。
EventUtil.addHandler(window, "load", function(){
document.getElementById("name-value").innerHTML = localStorage.getItem("name");
document.getElementById("book-value").innerHTML = localStorage.getItem("book");
EventUtil.addHandler(document.getElementById("delete-btn"), "click", function(){
//these don't work in webkit
//delete localStorage.name;
//delete localStorage.book;
localStorage.removeItem("name");
localStorage.removeItem("book");
});
EventUtil.addHandler(document.getElementById("see-btn"), "click", function(){
var i, key, value;
for (i=0, len = localStorage.length; i < len; i++){
key = localStorage.key(i);
value = localStorage.getItem(key);
alert(key + "=" + value);
}
});
//set some data
localStorage.setItem("name", "Nicholas");
localStorage.setItem("book", "Professional JavaScript");
});
存储在localStorage中的数据和存在globalStorage中的数据一样,都遵循相同的规则:数据保留到通过JavaScript删除或者是用户清楚浏览器缓存。
为了兼容只支持globalStorage的浏览器(因为chrome最新是不兼容globalStorage的,只实现了localStorage的),可以使用一下函数:
function getLocalStorage(){
if (typeof localStorage == "object"){
return localStorage;
} else if (typeof globalStorage == "object"){
return globalStorage[location.host];
} else {
throw new Error("Local storage not available.");
}
}
总结:有了上面这些选择,就可以在客户端机器上使用JavaScript存储大量数据了。 但你必须小心,不要在客户端存储敏感数据,因为数据缓存不会保密。