cookie session WebStorage
cookie和session
-
由来
Http是无状态协议 也就是说他不保存状态 不保存状态就是一个服务器是不清楚是不是同一个浏览器在访问 它也不保存之前发生过的请求和响应的状态 也就是说无法根据之前的状态进行本次的请求处理
cookie 之前 有另外的技术可以解决 就是在请求中插入一个 token 然后在发送请求的时候 把这个东西带给服务器 这种方式易出错 所以有了 cookie 的出现
如果希望一个页面能自动登录 但是又不能记录已登录状态 那么每一次重新跳转页面 不是要再次登录 就是要在每次请求报文中附加参数来验证登录状态
但是无状态协议也有好处 那就是Http由于不必保存状态 可以减少服务器内存资源的消耗 如果让服务器管理全部客户端状态会成为负担
于是为了保留无状态协议又能解决登录状态的问题 引入了cookie这一概念 通过在请求和响应报文中写入cookie信息来控制客户端状态
浏览器的缓存机制提供了将用户数据存储在客户端上的方式 因此可以利用cookie和session跟服务端进行数据交互
-
cookie
它根据服务器响应报文内set-cookie首部字段信息通知客户端以文本的形式保存cookie 这样能帮助服务器记住cookie是向谁发送的 下次客户端再往服务器发送请求的时候会自动在请求报文中加入cookie值之后发出 服务器能经过对比服务器上的记录得到之前的状态信息
-
cookie 的属性
-
name 这个显而易见 就是代表 cookie 的名字的意思
一个域名下绑定的 cookie name 不能相同 相同的 name 的值会被覆盖掉
-
value 这个就是每个 cookie 拥有的一个属性 它表示 cookie 的值
对于这个值有以下几种说法:
- cookie 的值必须被 URL 编码
- 对 cookie 的值进行编码不是必须的 还举了原始文档中所说的 仅对三种符号必须进行编码: 分号 逗号和空格
- 由于 cookie 规定名称/值是不允许包含分号 逗号 空格的 所以为了不给用户到来麻烦 考虑服务器的兼容性 任何存储 cookie 的数据都应该被编码
-
domain 是cookie绑定的域名 如果没有设置就自动绑定到当前URL的域 也就是二级域名
cookie保存在浏览器里并且按域名分类 不同的cookie会分配在不同的域名下保存 比如说百度的cookie不能存在淘宝的域名中
要注意 同一个域名下的二级域名也是不可以交换使用cookie的 例如www.baidu.com和image.baidu.com
domain默认为请求的地址 如网址为www.jb51.net/test/test.aspx 那么domain默认为www.jb51.net 而跨域访问 如域A为t1.test.com 域B为t2.test.com 那么在域A生产一个令域A和域B都能访问的cookie就要将该cookie的domain设置为.test.com 如果要在域A生产一个令域A不能访问而域B能访问的cookie就要将该cookie的domain设置为t2.test.com
在cookie相关文档信息中 提到cookie是不能跨域访问的 但是在二级域名是可以共享cookie的 这样就使我们的项目有了局限性 必须将多个系统的域名统一作为二级域名 统一平台提供使用主域名 这样就可以实现cookie的单点登录了
单点登录: 多个不同系统整合到统一加载个平台 用户在任何一个系统登录后 可以访问这个统一加载上的所有系统 登录之后 用户的权限和信息不再受某个系统的限制 即使某个系统出现故障(包括统一加载平台) 其他系统还是能正常使用的 这就需要用户权限等信息保存到客户端 不受服务器的限制
(扩展:一级域名和二级域名)
一级域名又称为顶级域名 大家需要注意的是www.lisp.com这种形式的域名并不是一级域名 它只是一个二级域名 也就是说www只是一个主机名
真正的一级域名是由一个合法的字符串+域名后缀组成 所以lisp.com这种形式的域名才是一级域名 lisp是域名主体 .com是域名后缀 可以是.net也是域名后缀
什么是二级域名 所谓的二级域名实际就是一个一级域名下面的主机名 顾名思义 它是在一级域名前面加上一个字符串 比如asdx.lisp.com 并且通过设置 可以拥有和跟顶级域名完全一样的功能
但这里并不是说一级域名与二级域名完全没有差别 二级域名与一级域名的差异: 二级域名是依附在一级域名的存在而存在的 也就是说顶级域名消失了 二级域名也会不复存在 反而来说 二级域名网站不做了 主域名不受影响的
-
解决顶级域名与二级域名之间的跨域问题
通过设置domain 顶级域名服务器与二级域名服务器之间哪个设置都能生效 用其中一个服务器设置完毕后写回到客户端 用另一个服务器即可访问此Cookie
cookie.setDomain("test.com") -
解决顶级域名于顶级域名之间的跨域问题
我们可以通过Nginx反向代理 将两个服务器域名统一到一个反向代理服务器
upstream www.test.com { server 127.0.0.1:8080 weight=1; server 127.0.0.1:8060 weight=1; } server { listen 80; server_name www.test.com; #charset koi8-r; #access_log logs/host.access.log main; location / { proxy_pass http://www.test.com; index index.html index.htm; } }使用ajax方式实现跨域访问
$.ajax({ type: 'GET', url: "http://www.cookie.test.com:8080/web2/ajaxServlet", data: {"username":"haha"}, dataType: "json", //设置crossDomain与withCredentials的作用 : //ajax默认不支持携带cookie 这里不开启是不能带cookie到后台的 //跨域请求为true 指示是否是跨域请求 这会让Request header中便会带上 Cookie 信息 如果你想在同一域中强制跨域请求请设置为true 这会允许服务器端重定向到另一个域发送Ajax时 crossDomain: true, xhrFields: { //为跨域请求设置XMLHTTPRequest对象 withCredentials: true }, success: function (data){ console.info(data); } });还可以使用jsonp格式发送
发送请求 希望拿到其他页面的cookie数据
要请求的页面的jsp如下 说明返回的是一段可以执行的js代码
请求结果如下: 返回是的可以执行的js代码 因为JSONP的请求地址是附加在script标签内的 所以浏览器可以自动执行这段代码并且设置该域的cookie
-
-
path这个属性默认是'/' 这个值匹配的是web的路由 举个例子
//默认路径 www.baidu.com //blog路径 www.baidu.com/blog我为什么说的是匹配呢 就是当你路径设置成/blog的时候 其实它会给/blog /blogabc等等的有"blog"字符串的路径绑定cookie
-
cookie的有效期
有效期就是图中的Expires属性 一般浏览器的cookie都是默认储存的 当关闭浏览器结束这个会话的时候 这个cookie也就会被删除 这就是上图中的——session(会话储存)
如果你想要cookie存在一段时间 那么你可以通过设置Expires属性为未来的一个时间节点 Expires这个是代表当前时间的 这个属性已经逐渐被Max-Age取代
所以我们可以设置 cookies 的Max-Age或者Expires到期时间
Max-Age是以秒为单位的 Max-Age为正数时 cookie会在Max-Age秒之后被删除 这时cookie会被保存在硬盘中 关闭浏览器后cookie数据仍然存在 直到过期事件结束才消失
如果不设置时间 Max-Age为负数 表示的是临时储存 不会生出cookie文件 只会存在浏览器内存中 且只会在打开的浏览器窗口或者子窗口有效 一旦浏览器关闭 cookie就会消失
当Max-Age为0时 会删除cookie 因为cookie机制本身没有设置删除cookie 失效的cookie会被浏览器自动从内存中删除 所以它实现的就是让cookie失效
-
secure
这个属性译为安全 http不仅是无状态的 还是不安全的协议 容易被劫持 当这个属性设置为true时 此cookie只会在https和ssl等安全协议下传输
小提示 这个属性并不能对客户端的cookie进行加密 不能保证绝对的安全性
-
HttpOnly
这个属性是面试的时候常考的 如果这个属性设置为true 就不能通过js脚本来获取cookie的值 能有效的防止xss攻击 看MDN的官方文档
-
-
关于js操作cookie
document.cookie可以对cookie进行读写 看下面两条指令:
//读取浏览器中的cookie console.log(document.cookie); //写入cookie document.cookie='myname=laihuamin;path=/;domain=.baidu.com'; -
提高cookie的安全性:
通过良好的编程控制保存在cookie中的session对象的大小
通过加密和安全传输技术减少cookie被破解的可能性
在cookie中存放不敏感的数据 即使被盗取也不会有很大的损失 控制cookie的生命期 使之不会永远有效 这样的话偷盗者很可能拿到的就是一个过期的cookie
-
cookie的优点:
具有极高的扩展性和可用性 兼容性比较好
-
cookie的缺点:
-
大小受限
-
用户可以操作(禁用)cookie 功能受限
如果用户禁用cookie 就要使用response.encodeURL(url)进行URL重写 当浏览器支持cookie时 url不做任何处理 当浏览器不支持cookie的时候 将会重写URL将sessionid拼接到访问地址后
-
安全性较低 xss注入攻击可以获取cookie cookie容易被复制
-
在与服务器端通信时 每次访问都要把cookie携带在HTTP头中传送给服务器 浪费带宽 而且如果使用cookie保存过多数据会带来性能问题
-
cookie数据有路径的概念 可以限制cookie只属于某个路径下 有域名限定 百度的域名只能装百度的cookie 但是不同的页面可以访问到其他页面的cookie
默认不可以跨域存储
-
有些状态不可能保存在客户端 例如 为了防止重复提交表单 我们需要在服务端保存一个计数器 若把计数器保存在客户端 则起不到什么作用
-
-
应用场景
- 比较常用的一个应用场景就是判断用户是否登录过 针对登录过的用户 服务器端会在他登录时往 Cookie 中插入一段加密过的唯一辨识单一用户的辨识码 下次只要读取这个值就可以判断当前用户是否登录过啦
- 保存上次登陆的时间等信息
- 保存上次查看的页面
- 浏览计数
- 在百度中登录之后 跳转到百度的其他页面还是可以拿到用户信息 是因为浏览器发送请求的时候带上了cookie 服务端解析之后返回了用户的信息 所以用这种方式可以保持登录状态
- 曾经还使用 Cookie 来保存用户在电商网站的购物车信息 如今有了 localStorage 似乎在这个方面也可以给 Cookie 放个假了~
-
-
session
先提一嘴基于表单认证 基于表单认证是HTTP/1.1使用的认证方式 客户端会向服务器上的web应用程序输入ID和密码来发送登录信息 按照登录信息的验证结果来决定认证是否成功 也就是说 它是在服务器端的web应用上 将客户端发送过来的ID和密码来和之前登陆过的信息进行匹配
使用cookie来管理session(会话) 是一种流行的基于表单验证的形式 但是鉴于HTTP是无状态协议 之前认证成功的用户状态无法通过协议来保存 所以这个用户下一次访问的时候我们也没办法区分他和其他的用户 于是我们使用cookie来管理session
session管理和cookie状态管理:
首先 客户端把用户ID和密码等登录信息放入报文体 (通常)再使用POST方法来把请求发送给服务器 这个时候会用HTTPS通信来发送用户输入的数据
当服务器收到请求需要创建session对象时 首先会检查客户端请求中是否包含Session ID 如果有Session ID 服务器将根据该id返回对应session对象
如果客户端请求中没有Session ID 服务器就发放用来识别用户的session ID 然后验证从客户端发过来的登录信息并进行身份认证 再把用户的认证状态和Session ID绑定起来记录在服务器端
响应客户端的时候 会在首部字段Set-Cookie中写入Session ID 如上图中的PHPSESSID=XXXX
在客户端接收到从服务器发送过来的Session ID之后会将其作为cookie保存在本地 再次向服务端发送请求的时候会自动发送cookie Session ID也会一起被发送到服务器 这时候就就可以通过验证Session ID来识别用户和用户状态
-
提高session的安全性
如果Session ID被盗走 对方就可以伪装身份进行操作 因此要进行有效期管理 Session ID也要使用难以被猜测的字符串
-
session的优点:
安全性:
- Session ID存储在cookie中 若要获取到session首先要攻破cookie
- 攻破了cookie也不一定能拿到session 因为Session ID要是有人登陆或启动session_start才会有
- 第二次启动session_start之前一次的Session ID就是失效了 session过期之后 Session ID也会失效
- Session ID是加密的
-
session的缺点:
-
session保存的东西越多越占用服务器内存
-
依赖于cookie 如果禁用cookie那么就要使用URL重写
-
[后端]创建session变量有很大的随意性 可以随时调用 不需要开发者做精确的处理 所以过度使用session会导致代码不可读而且不好维护
-
数据存在Session上也有缺点 如果用户量非常大上亿的用户 在用户量很大的时候 服务器端很耗资源 不仅是因为需要保存的东西太多 后端可能不止一台服务器 用户的登录信息一般只存在一台服务器上 (用户的登录操作在哪台机器上执行的就一般存在哪台机器上)
需要通过反向代理来查询用户保存的信息(轮询, IP哈希)
轮询:第一个请求给第一台 第二个请求给第二台.. 不能用session实现登录
ip哈希: 哪一个ip固定用哪一个服务器 保证登录信息存在
- 应用场景
- 网上商城中的购物车
- 保存用户登录信息
- 将某些数据放入session中供同一用户的不同页面使用
- 防止用户非法登录
-
-
cookie和session的区别
-
机制上的区别
-
保持状态上的区别
携带有(session ID 的) cookie保存在浏览器端 而不是存在页面上
session是保存在服务器端的 只要不关闭页面就一直保存这个链接
-
存储的大小上的区别 单个 cookies 保存的数据不能超过 4 K 很多浏览器限制一个站点保存最多 20个cookies (各浏览器不同) 而session没有大小限制
-
存储内容
cookie只能以文本形式保存字符串类型 但是session通过哈希表的数据结构来保存 可以支持任何类型的对象(session中可含有多个对象)
-
安全性:
session的安全性大于cookie 但是耗费资源
-
WebStroage
webStorage的目的是克服由cookie所带来的一些限制 当数据需要被严格控制在客户端的时候不需要持续的将数据发送回服务器
服务器端保存所有的用户的数据 所以服务器端的开销较大 而浏览器端保存则把不同用户需要的数据分布保存在用户各自的浏览器中
HTML5的WebStorage提供了两种API: localStorage(本地存储)和sessionStorage(会话存储)
Web存储是按来源(每个域和协议) 来自一个来源的所有页面都可以存储和访问相同的数据 对于不同的网站 数据存储于不同的区域 并且一个网站只能访问其自身的数据
主要目的:
- 提供一种在cookie之外存储会话数据的路径
- 提供一种存储大量可以跨会话存在的数据机制
-
localStorage
关闭页面或浏览器之后localStorage中的数据也不会消失 除非主动删除数据否则永远不会消失(除非用户设置了无痕模式)
一个页面里设置的数据 只要同个域下的页面均可以访问 不同页面始终是共享的 无论以何种方式打开的不同页面 当你无论以何种方式修改了localStorage 所有页面都会同步变化
window.localStorage或localStorage返回一个Storage对象 不能直接修改 只能操作它内部的属性 但是不能修改它的长度(length属性)两个不同的域名的localStorage不能直接互相访问 那么如何在domain2中如何调用domain1的localStorage?
-
在domain2.com的页面中 在页面中嵌入一个src为domain1.com的iframe 此时这个iframe里可以调用domain1.com的localStorage
用postMessage方法实现页面与iframe之间的通信
-
具体流程如下:
在domain2页面加载完成后使用postMessage方法向domain1中请求信息
在domain1中使用message监听domain2发送来的信息
domain1监听到信息后用postMessage方法发送自己的localStorage中的信息
在domain2中监听domain1传回来的信息 并且取出数据
兼容性:
-
-
sessionStorage
sessionStorage引入了一个"浏览器窗口"的概念 sessionStorage是在同源窗口中始终存在的数据 只要这个浏览器窗口没有关闭 即使刷新页面或者进入当前窗口的同源另一个页面 数据依然存在 而在关闭了浏览器窗口之后sessionStorage就会被销毁 同时打开同一个窗口的同一个页面 sessionStorage也是不一样的 因此sessionStorage不是一种持久化的本地存储 仅仅是会话级别的存储
不同浏览器写入数据方法略有不同 Firefox和Webkit实现了同步写入 所以添加到存储空间中的数据是立刻被提交的 而IE的实现则是异步写入数据 所以在设置数据和将数据实际写入磁盘之间可能有一些延迟
页面存储的数据只有同域的页面能够访问:
-
在当前窗口切换到同域页面的测试:
比如同域的两个页面demo-1.html demo-2.html 当前打开了demo1.html 并存储了一段数据A(细节略)接着在当前窗口切换到demo-2.html demo2.html里的脚本可访问A 如果此时切换到的不是demo- 2.html 而是其他域的页面 则无法访问A
测试:
在百度首页设置sessionStorage 随便搜索一个搜索词之后在同一个窗口跳转页面 可以共享sessionStorage 但是在tab栏点击百度知道后不能获取到设置的cookie 因二级域名不同
-
在不同窗口访问同域页面的测试:
同一个域名下的不同页面(注意为方便描述 以下所有a和b页面均在同一域名下)如Chrome浏览器中同一网站不同标签页 他们之间的sessionStorage是不会共享的 页面a变了 页面b不会同步 这一点官方文档上明确提出了规范
Opening a page in a new tab or window creates a new session with the value of the top-level browsing context, which differs from how session cookies work.在新选项卡或窗口中打开页面将创建一个具有顶级浏览上下文值的新会话,这与会话cookie的工作方式不同。除了不会共享之外 打开的新页面还会丢失已有页面的sessionStorage
也许开发中有人发现 页面a中打开的页面b 也能拥有a页面的sessionStorage
因此测试以下在页面a中打开b的打开方式之间的区别:
-
脚本window.open()打开新页面和ctrl + 鼠标点击
打开新页面会复用已有的sessionstorage 但是不会共用 因为第一个页面数据改了 第二个页面数据不会同步
-
鼠标右键弹出contextMenu 选择新页面打开 是不会复用sessionStorage的
-
打开一个空页面 然后复制b页面的url 这种方式打开新页面 是绝对不用复用已有的sessionStorage的 更别说共享了
-
-
使用:
window.sessionStorage或sessionStorage -
兼容性
-
-
storage的api
-
length属性当前Storage对象中存储的key/value对的总数
-
setItem(key, value)将key对象的值设置为value value为String类型
当设置失败 比如用户将本地存储禁用 或者本地存储超过限制的大小时 抛出QuotaExceededError错误
-
getItem(key)返回key对应的值 如不存在 返回null
-
removeItem(key)如果key在storage中存在 将key对应的key/value对从storage对象中删除 注意: 不会返回删除的 value
如果key在storage中不存在 不进行任何处理
-
clear()清除Storage对象上所有的key-value值
-
key(index)返回index对应位置的key值 当index>length时 返回null
往storage对象上添加key-value值 注意通过key(index)取回的键是不固定的 跟通过for...in遍历普通的对象一样 依赖于浏览器的实现
-
-
storage事件
根据标准的说明 当本地存储的数据发生变化时候 会触发storage事件 对应的事件对象包含了以下属性:
-
key被更新或删除的键 -
oldValue键更新前对应的value 如果键原先不存在 则oldValue为null -
newValue键更新后的value 如果键被删除 则newValue为null -
url storage事件发生的源(页面url) -
storageArae一个引用 指向当前发生变化的localStorage或sessionStorage
storage的触发事件的一些需要注意的地方
-
触发的条件
当本地存储的数据被修改的时候 会触发storage事件 并将事件放进任务队列里去
storage不会冒泡 也不可取消
在所有本地存储被影响到的window对象上触发
-
事件触发的对象
在demo-1.html上有如下代码
window.onstorage = function(){ console.log('storage changed in demo-1'); }; setTimeout(function(){ window.localStorage.intro = 'my name is casper'; }, 3000);在demo-2.html上有如下代码
window.onstorage = function(){ console.log('storage changed in demo-2'); };
3秒后会发生神马事情? 按照预计 demo-1.html demo-2.html页面的storage都被触发 并打印相关日志 但实际上 只有demo-2.html页面有日志打印出来 ```js “storage changed in demo-2”假如Storage对象X被修改 如添加删除key/value等,则在所有受到影响的文档的window对象上触发storage事件 除了X所在的那个文档 于是之前的现象就解释得通了 如果光看storage event的描述 很容易就掉坑里 很明显的前后不一致 据说在之前的版本里面这里的规定更加不明确 不想考古了 知道现在标准是怎么规定的就行了
-
-
localStorage和sessionStorage的共同点
-
存储大小
localStorage和sessionStorage的存储数据大小一般都是5MB
-
存储位置
localStorage和sessionStorage都保存在客户端 不和服务器通信
-
存储内容类型
只能存储字符串类型 复杂对象可以处理成json
-
-
localStorage和sessionStorage的区别
-
生命周期
localStorage的生命周期是永久的
sessionStorage的生命周期是仅在当前会话下有效
-
获取方式
localStorage:
window.localStoragesessionStorage:
window.sessionStorage -
作用域
相同浏览器的不同页面之间可以共享localStorage(只要在同样的域名的端口下)
但是不同页面或标签页之间无法共享sessionStorage的信息 只有同源同窗口才能共享
这里要注意的是页面和标签仅仅是指顶级窗口 如果一个标签页包含多个iframe标签而且他们属于同源的页面 那么他们之间是可以共享页面的
-
应用场景
localStorage:
-
用于长期登录或判断用户是否已登录等长期保存的数据
-
管理购物车的工作
-
HTML5游戏通常会产生一些本地数据 localStorage 也是非常适用的
-
一个详细的例子 京东官网顶部的广告关闭
效果为第一次进入官网会出现广告 然后点击关闭 刷新网页不会再显示广告 但是当清除localStorage存入的数据 刷新网页会再显示广告
sessionStorage:
- 敏感账号一次性登录
-
-
如果遇到一些内容特别多的表单 为了优化用户体验 我们可能要把表单页面拆分成多个子页面 然后按步骤引导用户填写 这时候 sessionStorage 的作用就发挥出来了
-
-
WebStorage的优点
-
存储空间更大 cookie是4kb 但是webStorage是5MB
-
节省网络流量 对于不需要交互的数据在cookie中保存后 cookie的内容会随着请求一并发送给服务器 这对于本地存储的数据是一种带宽浪费 而WebStorage不会传送到服务区 直接在本地获取 而且获取速度更快
-
sessionStorage可以用于在浏览页面期间保存 对于关闭浏览器之后就可以丢弃的页面会非常方便
-
安全性相对于cookie来说会高一些 不用担心发送的时候会被截取 因为不会被发送 但是任然存在伪造问题
-
WebStorage提供了一套更为丰富的接口 使得数据操作更为简便
-
每个域(包括子域)有独立的存储空间 各个存储空间是完全独立的 体验过同域下cookie相互覆盖导致的问题的童鞋应该深有体会
-
-
WebStorage安全性问题
使用它们的时候需要时刻注意是否有代码存在 XSS 注入的风险 因为只要打开控制台就可以随意修改它们的值 也就是说如果你的网站中有 XSS 的风险 它们就能对你的 localStorage 肆意妄为 所以千万不要用它们存储你系统中的敏感数据
-
localStorage sessionStorage Cookie共同点
都是保存在浏览器端 且同源的
参考书籍和网址:
- 一级域名和二级域名的区别
- cookie知识点概述
- 浅谈cookie session和localStorage sessionStorage的区别
- 本地存储sessionStorage localStorage cookie整理
- 详说 Cookie LocalStorage 与 SessionStorage
- localStorage sessionStorage Cookie的区别及用法
- 本地存储之WebStorage
- <<图解HTTP>> 上野宣(作者) 于均良(译者)
- 解决cookie跨域访问
- cookie 跨域访问的解决方案
- [Cookie]解决Cookie跨域访问
- HTML5 Web Storage
- [HTML5]Web Storage简析
- sessionStorage和localStorage在不同页面间的共享区别
- 不同域名之间共享localStorage/sessionStorage
- caniuse