[浏览器]带你掌握本地存储

406 阅读8分钟

最近想重新系统地整理一下前端的知识,因此写了这个专栏。我会尽量写一些业务相关的小技巧和前端知识中的重点内容,核心思想。

前言

本地存储是前端的一个重要知识,当今的web页面或多或少都会使用到本地记录功能。今天我们就来回顾一下前端本地存储的知识.帮助大家在开发时,在技术选择上有更清晰的思路。

本地存储的由来

最早的时候浏览器被设计为单纯的页面资源浏览工具。但随着页面需求的发展,我们有了用户记录的场景。但http协议设计本身是无状态的,也就是说服务端没办法知道每次请求的来访者是谁。这就需要客户端在每次请求时主动告诉服务端,自己的身份。这样就诞生了浏览器本地存储的需求。

目前浏览器本地存储的方案有cookie,localStorage,sessionStorage,indexedDB等。本文将描述这几种方案的实践使用,以及他们对于安全的对应考虑。

cookies

cookies是最早的浏览器存储方案,是为了解决购物车记录的场景而诞生的。它的诞生却开拓了浏览器本地存储的各种使用需求。最常见的就是用户记录,服务器生成当前用户标识,之后浏览器在每次请求时都带上,用户标识让服务器知道请求者是谁。这就解决了http无状态的短板。

使用

cookies的使用分为2种场景:服务器设置和本地设置。

// 服务器在响应头中可以通过Set-Cookie的字段,往浏览器上存数据
Set-Cookie: <cookie名>=<cookie值>

// 实例
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco      // 存储字段yummy_cookie
Set-Cookie: tasty_cookie=strawberry // 存储字段tasty_cookie

[页面内容]

而浏览器在每次请求时,都会自动带上cookie。这样服务器就可以获取这个请求的用户信息了。

GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

除此之外,浏览器还能通过JavaScript脚本主动设置cookies内容。

document.cookie = "yummy_cookie=choco";      // 写入新cookie
document.cookie = "tasty_cookie=strawberry"; // 写入新cookie
console.log(document.cookie);								 // 读取cookie
// logs "yummy_cookie=choco; tasty_cookie=strawberry"

大小限制

因为cookies在每次请求时都会自动带上,因此我们不可能让cookie无限的扩大,这会大大的影响我们的请求效率。因此cookies被设计成最大容量为4k左右(各厂商浏览器可能会不同)。设置大小的限制,虽然保证了我们的请求效率不会被大幅度的影响,但也限制了我们本地存储的信息量。

时效限制

既然大小的限制,对cookies来说是有利有弊的,我们得想一个办法可以更好地利用cookies,例如及时清理无用信息(过期信息)。因此cookies还支持时效的控制。从这个角度,cookies可以划分为会话期cookie和持久化cookie。通过Expires和Max-age字段区分。

  • 会话期cookie: 如果cookie在设置时,没有注明Expires或Max-age。这个cookie就是会话期的,当浏览器关闭之后它会被自动删除。
  • 持久化 cookie:一旦给cookie设置了Expires或Max-age字段之后,这个cookie就会被浏览器存储下来,就算关闭了浏览器,只要cookie没有过期就不会被删除。

注意:与网络缓存的强缓存一样,cookie的expires字段是http/1.0的内容有客户端与服务端的误差问题。而max-age是http/1.1的内容,值是一个固定的过期时间。现代浏览器几乎都使用max-age但往往页面还是会设置expires主要是为了向下兼容。在现代浏览器中如果cookie同时设置了max-age和expires,会忽略expires只关注max-age。

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; // 设置expires
Set-Cookie: id=a3fWa; max-age=3600;                          // 设置max-age

安全考虑

既然cookie担任的工作是那么的重要,自然也成了黑客们的攻击目标。之前的网络安全文章中我们也讲述了XSS和CSRF等攻击(点击回顾),他们都会尝试获取浏览器的cookie。那么在安全层面,cookies又有着哪些考虑呢?

限制访问

通过设置Secure 属性和HttpOnly 属性可以限制cookies的访问,

  • Secure:设置了Secure 属性之后,cookie只会在【https】请求中带上。
  • HttpOnly :设置了HttpOnly 属性之后,cookie只会在请求中被利用,客户端不能通过JavaScript脚本读写cookies

作用域限制

我们知道CSRF会通过第三方页面尝试利用户的登录状态,为此cookies也有相关的属性,防止在不同域名访问cookie。

  • Domain :Domain属性指定哪些域可以访问cookie,如果没有设置Domain,会默认以当前的origin为domain。因此设置Domain实际上比不设置的限制会更少。
// 假设当前页面的origin为 www.xxx.com
// 在不设置Domain的情况下,子域名tech.xxx.com是不能共享www.xxx.com的cookies内容的。
Domain=.xxx.org   // 设置之后tech.xxx.com可以共享www.xxx.com的cookies内容。
  • Path :path属性与Domain相似,只是path限制的是url的path部分。
// 假设当前请求的url为www.xxx.com/document
// 如果不主动设置path,浏览器默认cookie的path就是/document
path=/document // 当设置了path之后,子级path也能共享cookies。如/path/document/detail,/path/document/info等
  • SameSite:SameSite属性是相对比较新的内容,它提供3个属性None,Strict,Lax,直观地规范cookie的限制。目前现代浏览器给SameSite设置的默认值是lax,但一些老的浏览器是none。
None: 浏览器会在同站请求、跨站请求下继续发送 cookies,不区分大小写。
Strict: 最严格,浏览器将只在访问相同站点时发送 cookie
Lax:Strict 类似,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。

Web Storage

cookies是目前的使用历史最长的本地储存方案,但它仍然有着操作复杂,难以管理的问题。HTML5之后我们迎来了【Web Storage】。为前端的本地储存带来了新的改革。其中包括了【localStorage】和【sessionStorage】。

sessionStorage

sessionStorage存储在一个打开页面之后的活动空间,只要页面的标签没有就算刷新页面sessionStorage的数据仍然存在。当页面标签或者浏览器进程被关闭之后,数据就会清空。可以通过Window.sessionStorage访问。

// 保存数据到 sessionStorage
sessionStorage.setItem('key', 'value');

// 从 sessionStorage 获取数据
let data = sessionStorage.getItem('key');

// 从 sessionStorage 删除保存的数据
sessionStorage.removeItem('key');

// 从 sessionStorage 删除所有保存的数据
sessionStorage.clear();

大小限制

sessionStorage的大小限制一般为5M。

访问限制

sessionStorage的数据只存在于当前浏览器标签页。 即使相同的页面,在另一个标签页中打开也不会访问到同样的数据。 但是,它在同一标签页下来自相同的源的 iframe 之间sessionStorage是共享的。

localStorage

localStorage则是一个更持久的存储方案,只要不主动删除,会一直保存。可以通过Window.localStorage访问。

localStorage.setItem('myCat', 'Tom');
let cat = localStorage.getItem('myCat');
localStorage.removeItem('myCat');
localStorage.clear();

大小限制

同样localStorage的大小限制一般为5M。

访问限制

在同源的所有标签页和窗口之间共享数据。

IndexedDB

Web Storage解决了用户信息本地存储的问题,可是随着web场景的发展,我们对数据存储的要求逐渐提高。例如我们希望可以存储一些括文件/二进制大型对象(blobs)等。【IndexedDB】 是一个事务型数据库系统,类似于基于SQL的RDBMS,用于在客户端存储大量的结构化数据。

浏览器通过了一系列调用IndexedDB的底层 API,本文对于具体的操作不做过多的描述,有兴趣的朋友可以自行了解。目前也有很多专门对IndexedDB作了封装的工具库如localForage、dexie.js、PouchDB、idb、idb-keyval、JsStore 或者 lovefield等。

// 示例
var db;
function openDB() {
 var DBOpenRequest = window.indexedDB.open('toDoList');
 DBOpenRequest.onsuccess = function(e) {
   db = DBOpenRequest.result;
 }
}

大小限制

对IndexedDB数据库大小的唯一限制是用户的磁盘空间和操作系统

访问限制

IndexedDB 遵守同源策略。在某个域名下操作储存数据的时候,不能操作其他域名下的数据。

cookies会被淘汰吗?

这是一个有争议的问题,因为cookie有着一些特殊的机制(可以在服务端设置信息,在请求时自动带上,有现成的属性配置安全限制等)。因此有人认为cookie可以专注做用户权限记录的工作,而web storage可以专注做本地信息存储的功能。

但是近年来,由于对cookies的滥用导致了用户隐私问题频发。出于隐私保护的缘故,浏览器取消对 Cookie 的支持已经是大势所趋。但是尽管web storage对本地信息存储的效果很优秀,但在一些安全防范层面(XSS),他的效果并没有cookie好。但随着技术的发展,相信在cookie完全淘汰之前,一定会有新的方案出现,我们可以一起关注。

总结

今天我们了解了前端本地储存的知识,从使用和效率限制,安全限制的角度描述了cookie,web storage,indexedDB的等技术。希望可以帮助大家加深印象,在工作中灵活运用。同时我们也讨论了一下,cookie是否会被淘汰的问题.

参考

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zh-CN/docs/…