在Web应用开发中,如何选择一个合适的Web存储方案呢?文章介绍了Cookie、WebStorage和IndexedDB等常见的Web存储技术。包括它们的技术背景、原理和应用场景,理解这些知识将有助于我们制定更合理的Web方案。
Cookie解决了HTTP协议无状态
的问题:即HTTP请求/响应本身不保存会话状态。对于交互式Web应用,会话状态一般会随着用户操作发生改变的。例如以下场景:
用户登录状态:当用户登录后,刷新或进入下一页面时,服务端如何判断当前请求的用户登录状态呢?
购物车:当用户选中商品后,进入下个页面继续购物时,服务端如何获取用户在上一页面选中的商品呢?
而Cookie就是解决HTTP协议无状态的一种方法。Cookie是存储在浏览器本地的数据,通过HTTP请求报文头传递到服务端
。以上面的用户登录状态为例:
当用户登录后,服务端生成一个Cookie并返回给浏览器,浏览器下次请求时带上Cookie,这样服务端就可以通过Cookie信息判断用户登录状态。
PS:一般把真实信息存放在数据库,然后关联一个token存放到Cookie中。
Cookie是服务端返回给浏览器并保存在本地的数据
,浏览器下次请求同一服务端时会携带Cookie传递给服务端
。通常,它用于告知服务端两个请求是否来源于同一浏览器
。
常见应用场景:
当服务端接收到 HTTP 请求时,可以通过 Set-Cookie
响应报文头设置Cookie信息。浏览器收到响应后会保存 Cookie,之后对该服务端的每一次请求都通过 Cookie
请求报文头携带Cookie信息发送给服务端。
响应报文:
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
[页面内容]
请求报文:
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
PS:浏览器端也可以通过document.cookie api读写cookie。
Expires
或Max-Age
。Expires
或Max-Age
后才失效。示例:
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
domain
和 path
定义了Cookie的作用域:即允许 Cookie 被发送给哪些URL。domain默认是origin的域名。
示例:
set-cookie: token=123; domain=.bing.com;path=/;
Cookie可以在子域名之间共享
。例如,单点登录
:用户在门户网站boss.com登录后保存Cookie,之后进入子网站a.boss.com时会携带Cookie,服务端就知道是哪个用户了。
Cookie禁止跨域
。当domain指定为第三方域名时无效。例如:在a.example.com网站设置domain为b.example.com时无效。
与Cookie相关的安全问题有:man-in-the-middle attack
、XSS
和CSRF
。对应的Cookie安全属性有:
防止man-in-the-middle attack。
Cookie通过HTTP报文在浏览器和服务端之间传输
,有man-in-the-middle attack的风险。而 Secure属性要求Cookie 只能被 HTTPS请求携带,从而保证Cookie的安全传输。
但是,只要能访问到客户端硬盘的人依然可以读取到Cookie。所以Cookie不应该携带敏感信息
。
防止XSS。
HttpOnly属性表示不允许浏览器端脚本读写Cookie,这可以防止XSS
(跨站脚本攻击)。
PS:Cookie本职工作就是帮助服务端辨别请求来源的,对浏览器端程序应该是透明的,这种情况应该带上HttpOnly属性。
防范第三方Cookie存在的CSRF风险
。
当Cookie的domain属性不是当前网站域名和父级域名时,称为第三方Cookie
。第三方Cookie存在CSFR风险。示例:
Set-Cookie:token=jeif;domain=bank.example.com
<img src="http://bank.example.com/transferAccount" />
SameSite有以下三个值:
None
:不限制第三方Cookie。
Strict
:只有在domain指定域名中发起的请求才能携带Cookie。
Lax
(默认值),与 Strict
类似,但允许在第三方网站中导航至目标URL时携带Cookie。
Strict是严格限制第三方Cookie的,这会导致从外部站点导航至目标URL时,都要重新登录。默认使用Lax,允许在第三方网站中,导航到目标网站的get请求携带Cookie,例如、和
示例:
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly;SameSite=Lax;
虽然第三方Cookie存在CSRF风险,但在正常应用中还是有很大的价值的,常用来跟踪用户行为。例如:单点登录和广告精准投放。示例:
浏览器对每个站点下的Cookie大小和数量都是有限制的。每个cookie的name=value
的value值大概在4k
。
由于服务器设置 Cookie 后,浏览器的每次请求都会携带 Cookie 数据,会带来额外的性能开销。
总结:Cookie的本职工作是维持HTTP状态
,并不适合复杂的存储需求。
过去,Cookie作为惟一的浏览器端存储方案,存储容量小且带来了额外的性能开销
,并不适合所有的存储需求。更多时候,我们需要的是一个单纯的客户端存储功能:即数据存储在浏览器端,而不需要在浏览器和服务端之间来回传递。例如实现以下功能:
现在,Web Storage提供了更专业的浏览器端存储方案。
Web Storage通过创建一个Storage对象来存储键值对,key和value是字符串格式。并且该Storage对象只保存在浏览器本地,不会通过HTTP请求传递给服务端。
示例:
// 1.存储值
localStorage.setItem("key","value");
// 2.获取值
var valueLocal = localStorage.getItem("key");
// 3.删除值
Storage.removeItem("key");
// 4.清除Storage对象
Storage.clear();
// 5.Storage对象发送变化时,触发storage事件
window.addEventListener('storage', function(e) {
document.querySelector('.my-storage').textContent = e.storageArea;
});
sessionStorage
:Storage对象仅在页面会话期间有效。localStorage
:Storage对象是持久性的。在浏览器重新打开时依然有效。Storage对象最大容量为5M左右。
Web Storage的作用域为同一origin。其中sessionStorage只作用域当前页面,即便打开两个标签页访问同一页面,也会创建两个不同的Storage对象。
Web Storage的应用场景:
localStorage实现用户搜索历史记录功能。
localStorage实现存储客户端配置信息。
sessionStorage可以作为会话级别的缓存,例如表单信息存储,实现刷新页面不丢失表单数据。
虽然 Web Storage
在存储较少量的数据很有用,但对于存储更大量的结构化数据来是说力不从心的。而 IndexedDB 提供了这种场景的解决方案。
IndexedDB 是一个非关系型数据库
,可以存储结构化克隆算法支持的任何对象(包括字符串、ArrayBuffer 对象和 Blob 对象等二进制数据)。支持索引检索。
使用过程包括:指定数据库模式,建立数据库连接,然后检索和更新一系列事务
。由于IndexedDB提供的是比较底层的api,所以开发中可以借助第三方库来操作。
Cookie 解决的是HTTP协议无状态的问题;
Web Storage 提供的纯浏览器端存储的功能,不涉及跟服务端交互;
IndexedDB 是更专业的浏览器端存储方案,支持更大的存储容量,更复杂的数据结构。
正是有了这些更专业的浏览器端存储方案的出现,让Web应用的性能进一步的提升,可以承载更加复杂的业务功能。
mdn
Cookie 已凉,Web 存储该这么做!