浏览器存储
随着Web应用程序的出现,直接在客户端存储用户信息的需求也随之出现。与特定用户相关的信息应该保存在用户的机器上。无论是登录信息,个人偏好,还是其他数据,Web应用程序都需要有办法把它们保存在客户端。
对于该问题的第一个解决方案就是使用到了cookie,cookie由网景公司发明,由一份名为Persistent Client State:HTTP Cookies的规范定义,今天,cooike只是在客户端的存储数据的一个选项。
一.cookie
1.cookie的来源
Cookie 的本职工作并非本地存储,而是“维持状态”。 因为HTTP协议是无状态的,HTTP协议自身不对请求和响应之间的通信状态进行保存,通俗来说,服务器不知道用户上一次做了什么,这严重阻碍了交互式Web应用程序的实现。在典型的网上购物场景中,用户浏览了几个页面,买了一盒饼干和两瓶饮料。最后结帐时,由于HTTP的无状态性,不通过额外的手段,服务器并不知道用户到底买了什么,于是就诞生了Cookie。它就是用来绕开HTTP的无状态性的“额外手段”之一。服务器可以设置或读取Cookies中包含信息,借此维护用户跟服务器会话中的状态。
我们可以把Cookie 理解为一个存储在浏览器里的一个小小的文本文件,它附着在 HTTP 请求上,在浏览器和服务器之间“飞来飞去”。它可以携带用户信息,当服务器检查 Cookie 的时候,便可以获取到客户端的状态。
在刚才的购物场景中,当用户选购了第一项商品,服务器在向用户发送网页的同时,还发送了一段Cookie,记录着那项商品的信息。当用户访问另一个页面,浏览器会把Cookie发送给服务器,于是服务器知道他之前选购了什么。用户继续选购饮料,服务器就在原来那段Cookie里追加新的商品信息。
结帐时,服务器读取发送来的Cookie就行了。
2.什么是cookie及应用场景
Cookie指某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密)。 cookie是服务端生成,客户端进行维护和存储。
通过cookie,可以让服务器知道请求是来源哪个客户端,就可以进行客户端状态的维护,比如登陆后刷新,请求头就会携带登陆时response header中的set-cookie,Web服务器接到请求时也能读出cookie的值,根据cookie值的内容就可以判断和恢复一些用户的信息状态。
Cookie 以键值对的形式存在。
典型的应用场景有:
- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息);
- 个性化设置(如用户自定义设置、主题等);
- 浏览器行为跟踪(如跟踪分析用户行为等)。
3. Cookie的原理及生成方式
Cookie的原理
第一次访问网站的时候,浏览器发出请求,服务器响应请求后,会在响应头里面添加一个Set-Cookie选项,将cookie放入到响应请求中,在浏览器第二次发请求的时候,会通过Cookie请求头部将Cookie信息发送给服务器,服务端会辨别用户身份,另外,Cookie的过期时间、域、路径、有效期、适用站点都可以根据需要来指定。
Cookie的生成方式主要有两种:
- 生成方式一:http response header中的set-cookie
我们可以通过响应头里的 Set-Cookie 指定要存储的 Cookie 值。默认情况下,domain 被设置为设置 Cookie 页面的主机名,我们也可以手动设置 domain 的值。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2018 07:28:00 GMT;
//可以指定一个特定的过期时间(Expires)或有效期(Max-Age)
- 生成方式二:js中可以通过document.cookie可以读写cookie,以键值对的形式展示
例如我们在掘金社区控制台输入以下三句代码,便可以在Chrome 的 Application 面板查看生成的cookie:
document.cookie="userName=hello"
document.cookie="gender=male"
document.cookie='age=20;domain=.baidu.com'
从上图中我们可以得出:
Domain 标识指定了哪些域名可以接受Cookie。如果没有设置domain,就会自动绑定到执行语句的当前域。 如果设置为”.baidu.com”,则所有以”baidu.com”结尾的域名都可以访问该Cookie,所以在掘金社区上读取不到第三条代码存储Cookie值。
4. cookie的缺陷
-
cookie不够大
cookie的限制(通常,只要遵守以下大致的规则,就不会再任何浏览器中碰到问题)
- 不超过300个cookie
- 每个cookie不超过4096字节( 4 k b )=>一个二进制位=1位,8位(bit)=1字节,1024字节=1 k b
- 每个域不超过20个cookie;
- 每个域不超过81920字节。
每个域能设置的cookie总数也是受限的,但不同的浏览器的限制不同,例如:
最新版IE和Edge限制每个域不超过50个cookie;
最新版Firefox限制每个域不超过150个cookie;
最新版Opera限制每个域不超过180个cookie;
Safari和Chrome对每个域的cookie数没有硬性限制
如果cookie总数超过了单个域的上限,浏览器就会删除之前设置的cookie.IE和Opera会按照最近最少使用原则删除之前的cookie,以便为新设置的cookie腾出空间。Firefox好像会随机删除之前的cookie,因此为避免不确定的结果,最好不要超出限制。
- 过多的 Cookie 会带来巨大的性能浪费
Cookie 是紧跟域名的。同一个域名下的所有请求,都会携带 Cookie。大家试想,如果我们此刻仅仅是请求一张图片或者一个 CSS 文件,我们也要携带一个 Cookie 跑来跑去(关键是 Cookie 里存储的信息并不需要),这是一件多么劳民伤财的事情。Cookie 虽然小,请求却可以有很多,随着请求的叠加,这样的不必要的 Cookie 带来的开销将是无法想象的。
cookie是用来维护用户信息的,而域名(domain)下所有请求都会携带cookie,但对于静态文件的请求,携带cookie信息根本没有用,此时可以通过cdn(存储静态文件的)的域名和主站的域名分开来解决。
- 由于在HTTP请求中的Cookie是明文传递的,所以安全性成问题,除非用HTTPS。
5. cookie与安全
对于 cookie 来说,我们还需要注意安全性。
HttpOnly 不支持读写,可以在浏览器设置,也可以在服务器设置,但是只能在服务器读取。因为浏览器不允许脚本操作document.cookie去更改cookie, 所以为避免跨域脚本 (XSS) 攻击,通过JavaScript的 Document.cookie API无法访问带有 HttpOnly 标记的Cookie,它们只应该发送给服务端。如果包含服务端 Session 信息的 Cookie 不想被客户端 JavaScript 脚本调用,那么就应该为其设置 HttpOnly 标记。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
标记为 Secure 的Cookie只应通过被HTTPS协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过Cookie传输,因为Cookie有其固有的不安全性,Secure 标记也无法提供确实的安全保障。
为了弥补 Cookie 的局限性,让“专业的人做专业的事情”,Web Storage 出现了。
这样有了WebStorage后,
cookie能只做它应该做的事情了——作为客户端与服务器交互的通道,保持客户端状态。
二,WebStorage
两个目标:
-
提供在cookie之外的存储会话数据的途径
-
提供跨会话持久化存储大量数据的机制
两个对象
-
localStorage永久存储机制
-
sessionStorage跨会话的存储机制,浏览器关闭则数据消失, 需要注意的是 sessionStorage 的作用域是窗口级别的,也就是说不同窗口之间保存的 sessionStorage 数据是不能共享的。
三,localStorage
一种持久化的存储方式,也就是说如果不手动清除,数据就永远不会过期。它是采用键值对的方式存储数据,按域名将数据分别保存到对应数据库文件里。相比 Cookie 来说,它能保存更大的数据。
localStorage 的特点:
-
大小限制为 5MB ~10MB;
-
在同源的所有标签页和窗口之间共享数据;
-
数据仅保存在客户端,不与服务器进行通信;
-
数据持久存在且不会过期,重启浏览器后仍然存在;
-
对数据的操作是同步的。
示例:
// 通过setItem()增加一个数据项
localStorage.setItem('myName', 'Semlinker');
// 通过getItem()获取某个数据项
let me = localStorage.getItem('myName');
// 通过removeItem()移除某个数据项
localStorage.removeItem('myName');
// 移除所有数据项
localStorage.clear();
四. sessionStorage
与服务端的 session 类似,sessionStorage 是一种会话级别的缓存,关闭浏览器时数据会被清除。需要注意的是 sessionStorage 的作用域是窗口级别的,也就是说不同窗口之间保存的 sessionStorage 数据是不能共享的。
sessionStorage 的特点:
- sessionStorage 的数据只存在于当前浏览器的标签页;
- 数据在页面刷新后依然存在,但在关闭浏览器标签页之后数据就会被清除;
- 与 localStorage 拥有统一的 API 接口;
- 对数据的操作是同步的。
「示例」
// 通过setItem()增加一个数据项
sessionStorage.setItem('myName', 'Semlinker');
// 通过getItem()获取某个数据项
let me = sessionStorage.getItem('myName');
// 通过removeItem()移除某个数据项
sessionStorage.removeItem('myName');
// 移除所有数据项
sessionStorage.clear();
五.Web SQL
Web SQL 数据库 API 实际上不是 HTML5 规范的一部分,而是一个单独的规范,它引入了一组 API 来使用 SQL 来操作客户端数据库。需要注意的是,HTML5 已经放弃 Web SQL 数据库。
六. IndexedDB
IndexedDB 是一种底层 API,用于客户端存储大量结构化数据,包括文件、二进制大型对象。该 API 使用索引来实现对该数据的高性能搜索。虽然 Web Storage 对于存储较少量的数据很有用,但对于存储更大量的结构化数据来说,这种方法不太好用。IndexedDB 提供了一个解决方案。
IndexedDB 的特点:
-
存储空间大:存储空间可以达到几百兆甚至更多;
-
支持二进制存储:它不仅可以存储字符串,而且还可以存储二进制数据;
-
IndexedDB 有同源限制,每一个数据库只能在自身域名下能访问,不能跨域名访问;
-
支持事务型:IndexedDB 执行的操作会按照事务来分组的,在一个事务中,要么所有的操作都成功,要么所有的操作都失败;
-
键值对存储:IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以 “键值对” 的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
-
数据操作是异步的:使用 IndexedDB 执行的操作是异步执行的,以免阻塞应用程序。
「示例」
var dbName = "my_db";
var request = indexedDB.open(dbName, 2);
request.onerror = function(event) {
// 错误处理
};
request.onupgradeneeded = function(event) {
var db = event.target.result;
// 建立一个对象仓库来存储我们客户的相关信息,我们选择 ssn 作为键路径(key path)
// 因为 ssn 可以保证是不重复的
var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });
// 建立一个索引来通过姓名来搜索客户。名字可能会重复,所以我们不能使用 unique 索引
objectStore.createIndex("name", "name", { unique: false });
// 使用邮箱建立索引,我们确保客户的邮箱不会重复,所以我们使用 unique 索引
objectStore.createIndex("email", "email", { unique: true });
// 使用事务的 oncomplete 事件确保在插入数据前对象仓库已经创建完毕
objectStore.transaction.oncomplete = function(event) {
// 将数据保存到新创建的对象仓库
var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");
customerData.forEach(function(customer) {
customerObjectStore.add(customer);
});
};
};