Cookie&Session、LocalStorage&SessionStorage、HTTP缓存
<pre>
标签可以保留回车和空格等你怎么写它就怎么展示的内容
cookie
cookie
可以看作是一种设置,允许浏览器在电脑本地硬盘的某一个隐蔽的地方开发一块存储空间,用来存放某些特定的内容。
如果在服务器端设置了允许使用cookie
,那么,之后浏览器每次向同域名服务器发送请求,都会带上cookie
,意味着每发送一次请求,浏览器都会把存在本地硬盘那某一个隐蔽地方里的文件发送给服务器,交由服务器处理。
通常,我们习惯将用户的登陆信息存放在cookie
里,所以,服务器能够通过浏览器发送给它的cookie
判断用户注册了没有?、用户名密码是否正确?、用户名密码是否匹配?、该用户多少等级了?。。。由此,返回相应的网页内容。
在这里为什么总感觉难以理解??这是因为,cookie
的概念要涉及服务器和浏览器,两者感觉交织在一起。所以,有必要理清一下思路:cookie的基本操作流程。
cookie的基本操作流程
- 第一,浏览器会发送一个请求,将一个东西(对象、哈希、数组,随便你怎么叫)发送给服务器
- 第二,服务器将发送给它的这个东西进行处理,得到的结果以字符串的形式、通过
Set-Cookie
响应头返回给浏览器,该字符串称之为cookie
- 第三,现在浏览器得到
Set-Cookie
响应头并获允使用cookie
,之后,用户每次向相同域名网站的服务器发送的请求,都会带上该cookie
- 第四,每一次请求,服务器可以读取
cookie
,获取cookie
中包含的信息(用户资料、特定页面信息等),然后向浏览器返回相应的内容
cookie示例
前端代码:
$.post('/sign_in', hash) .then((response)=>{ window.location.href = '/index.html' }, (request)=>{ alert('邮箱与密码不匹配') })
使用node.js
写的后端代码示意:
if(path === '/sign_in' && method === 'post'){ //your code response.setHeader('Set-Cookie','sign_in_email=${email}'; HttpOnly; Max-Age=3000'); //your code }else if(path === '/index.html'){ //your code let cookies = request.headers.cookie; //your code }
在上述示例代码中,
- 第一步,前端代码(浏览器)使用
jquery
的post()
方法向服务器朝/sign_in
路径发送了一个请求,将变量hash
传给了服务器。 - 第二步,后端代码(服务器)在
//your code
里对变量hash
处理后,将处理结果变成'sign_in_email=${email}'
的字符串(其中${email}
表示变量email代表的数据),然后设置了响应头Set-Cookie
返回给浏览器,并加诸了像HttpOnly
、Max-Age
等各类设置(详细说明请参见https://developer.mozilla.org...)。 - 第三步,浏览器收到了服务器返回的响应头
Set-Cookie
和cookie
(字符串),说明请求成功了,会将网页重定向为/index.html
(这里使用了promise
:.then((response)=>{window.location.href = '/index.html'
)。所以浏览器又向服务器发送了一次请求。这次请求会带上cookie
- 第四步,服务器又收到了一次请求,这次请求的路径是
/index.html
,并且带有cookie
。那么,服务器可以使用request.headers.cookie
来获取cookie
,然后在//your code
里处理后,在//your code
里给浏览器返回相应的页面
cookie的几个特点
不同浏览器之间的
cookie
不通用这就好像Firfox的www.segmentfault.com不是chorme的www.segmentfault.com
cookie
存在硬盘的某一个神秘文件里cookie
很容易被修改,用户可以自己进入浏览器控制台修改cookie
看到下图,Firfox的控制台进入存储、进入Cookies,我们修改了两个值,并且刷新后,一个值会变回来,一个值没有变回来
cookie
的有效期默认由浏览器自己决定,当然可以通过后端设置cookie
的保存时间当然,不同后端语法不一样写法不一样,通常都是设定Max-Age或者Expires属性
详细可以参见:
Set-Cookie:https://developer.mozilla.org...
HTTP cookies:https://developer.mozilla.org...
cookie应用
可想而知,cookie
最常用的就是注册&登陆啦~~~
- 先在浏览器注册,注册好了就向浏览器发送请求,报告!请求注册成功页面!!
- 服务器检查下自己的数据库,这是个新兵,存下来存下来,然后返回包含新兵狗牌的
cookie
和注册成功页面 - 这个
cookie
有时间限制,在这个时间段里,新兵访问服务器不用再报告了(浏览器发送请求一直带着这个cookie
) - 过了这个时间段,
cookie
失效了(浏览器发送请求不带上cookie
了),不好意思,请证明你自己(登陆,并获得新的老兵狗牌的cookie
和登陆成功页面)
session
cookie
好啊,可以让服务器知道我们是VIP用户了,不过因为能被轻松的查看并且容易被篡改,所以引出新概念session
,而session
更像是一种技术,而不是一种设置
从前,服务器直接将用户信息存在cookie
里,现在,服务器将sessionId
放在cookie
里,再通过sessionId
到session
表里查找sessionId
对应的相关内容。
那为什么就防止了cookie
容易被篡改的问题呢?因为sessionId
里存放的是随机数Math.random()
,你取个很多位的随机数,那普通人就没办法猜了,完全不知道哪个随机数对应的是用户、哪个不是。
那要是sessionId
被删了呢?那没办法了,只能重新登陆,意味着重新提交、重新分配随机数。
看上图,上图是在chorme
里控制台的Application → Storage → Cookies
选项,看到服务器向浏览器发送了带有sessionId
的cookie
,一个随机数,之后,浏览器再向服务器发送请求就会带上这个cookie
session
其实本质上就是cookie
,只不过加了一个中间量sessionId
,我们还是来看看session的基本流程
session的基本流程
- 第一,浏览器会发送一个请求,将一个东西(对象、哈希、数组,随便你怎么叫)发送给服务器
- 第二,服务器有一个哈希叫作
session
,该哈希的key
就是sessionId(随机数)
、value
是第一步里送送给服务器的东西的处理结果:一个字符串,以前的cookie
- 第三,服务器会设置响应头
Set-Sookie
,将sessionId(随机数)
通过cookie
的形式发送给浏览器 - 第四,之后,浏览器每请求一次服务器,就会带上这个含有
sessionId
的cookie
,服务器就会读取sessionId
,并在session
哈希里查找sessionId
对应的值,然后作出相应的操作
session示例
前端代码:
$.post('/sign_in', hash) .then((response)=>{ window.location.href = '/index.html' }, (request)=>{ alert('邮箱与密码不匹配') })
使用node.js
写的后端代码示意:
let sessions = {}; if(path === '/sign_in' && method === 'post'){ //your code let sessionId = Math.random()*100000000; session[sessionId] = {sign_in_email:email}; response.setHeader('Set-Cookie','sessionId=${sessionId}'; HttpOnly; Max-Age=3000'); //your code }else if(path === '/index.html'){ //your code let cookies = request.headers.cookie; let sessionId = ; let email = session[sessionId]; //your code }
上面发生了什么可以通过和cookie
作对比知道:
- 第一,在服务器我们设置了一个哈希
let session = {}
- 第二,在服务器我们生成一个随机数
sessionId
作为session
的键,获取email
作为该键的值 - 第三,服务器设置
Set-Cookie
响应头,将sessionId
作为cookie
传回给浏览器 - 第四,浏览器请求成功,网页重定向为
/index.html
,重新发送请求,带有cookie
,其中带有sessionId
- 第五,服务器接收到
cookie
,得到sessionId
,搜寻session
,获得相应email
,在//your code
里返回相应内容
localStorage
localStorage
是HTML5发布的新api。它是一个哈希,作用就是字面意思,本地存储,只不过这里的本地指的是浏览器。
请参考:https://developer.mozilla.org...
用法也不难,你可以通过localStorage
自己的方法往这个哈希里面的数据,再通过localStorage
自己的方法调用里面的数据。
localStorage的方法
设置一个
localStorage
值:setItem
localStorage.setItem("cat","rainy");
获取一个
localStorage
值:getItem
var cat = localStorage.getItem("cat");
移除一个
localStorage
值:removeItem
localStorage.removeItem("cat");
清除所有
localStorage
值:clear
localStorage.clear();
查看
localStorage
哈希:localStorage
console.log(localStorage);
localStorage的特点
- 和
http
无关,意味着它不会存在于浏览器和服务器之间的通信,请求响应时不会带上localStorage
的值 - 只有相同域名的页面才能互相读取
localStorage
- 当然,
localStorage
是浏览器自己的存储空间,所以不同浏览器之间也是不能相互读取的 - 当页面刷新或者关闭后,
localStorage
里的值也不会消失,所以叫local呀~ localStorage
的物理地址存在硬盘里的某个文件里- 每个域名的
localStorage
最大存储空间都是浏览器自定的,一般在5MB左右,如果溢出就会有下面这样的提示 - 永久有效,除非手动清理
sessionStorage
此接口作用和localStorage
一样样,也是开辟了一块地方供浏览器存储数据用。
请参考:https://developer.mozilla.org...
sessionStorage
的方法请参考上一章localStorage的方法。请将localStorage
都替换成为sessionStorage
。
sessionStorage
的特点请参考上一章localStorage的特点。唯一的区别在于sessionStorage
在关闭页面后就被清空了。请看下动图。
小结
形象理解
cookie
&session
就好像要去游乐园(服务器)玩,你可以选择买票(登陆,获得
cookie
)或者不买票(不登陆,随便逛逛),不买票只能玩一些项目(网页公共内容),买了票能解锁更多项目(网页私有内容)。那么关于这张票,如果实名认证的,你的姓名、身份证号都在上面,这是cookie
的做法,如果票上面只有一个编号,游乐园需要通过编号查找数据库才能认证你,那这就是session
的做法cookie
和session
有啥区别?session
是基于cookie
实现的。session
就是不直接将用户信息存放在cookie
里,而是将sessionId
放在cookie
里传给服务器,服务器通过sessionId
在session
哈希里查找相应的值cookie
和localStorage
有啥区别?cookie
会随着每一次请求发送给服务器,而localStorage
则不会带给服务器,它是浏览器的一块存储地。另外,cookie
一般只有5KB左右的大小,而localStorage
一般则有5MB左右的大小sessionStorage
和localStorage
有啥区别?sessionStorage
在页面关闭(会话结束)后就被全部清空,而localStorage
则不会。- 作为前端,最好不要直接读/写
cookie
。cookie
的内容越多,发送给服务器的时间越长,影响请求时间,导致访问变慢。如果一般的数据,不需要特别发给服务器的,请使用localStorage
http缓存
Cache-Control
顾名思义,控制缓存
若服务器设置了该项设置,意味着页面将被放在缓存里,当浏览器需要请求服务器的时候,将不会将请求发送至服务器,而是直接调用缓存里的页面。
各类属性详细请参考:https://developer.mozilla.org...
请看下图,右侧浏览器Chorme向服务器Server请求/main.js
,服务器Server返回浏览器Chorme一个Max-Age=30
的Cache-Control
的响应头。那么接下来的30s内,浏览器Chorme再向服务器Server请求/main.js
,注意!!!必须是完全相同的URL!!!,会直接从缓存(内存)里调用main.js
,直到过了30s从能再从服务器端获取main.js
Cache-Control的使用
前端代码:
$.post('/sign_in', hash) .then((response)=>{ window.location.href = '/index.html' }, (request)=>{ alert('邮箱与密码不匹配') })
使用Node.js写的后端:
if(path === '/sign_in' && method === 'post'){ //your code response.setHeader("Cache-Control","max-age=30"); //your code }
上面的代码显示,在30s的时间内,任何以post方法向服务器/sign_in路径的请求,都不会被发送,而会直接调用缓存里/index.html的页面
Cache-Control的几个特点
- 通常首页请不要设置缓存,如果设置了,就意味着做任何操作后,页面都从缓存里调用,这样就不会再更新新的页面。
除去首页,其它资源可以设置为10年,300000000,3后面8个0
什么?10年?那需要更改了怎么办?
修改url
原来页面里引用的js
例如说像这样:<script src="./myCatRainy.css"></script>
那么现在只需要改正这样:<script src="./myCatRainy.css?v=1"></script>
看出区别了吗?多了?/v=1
,但是两个路径都调用的同一个文件- 同样,
Cache-Control
控制的缓存的物理地址在硬盘里的某一个位置,浏览器会设置一个固定大小,多了就将之前的缓存清掉
Expires
曾经,我们使用Expires
设置缓存控制响应头:
if(path === '/sign_in' && method === 'post'){ //your code response.setHeader("Expires","Sun, 04 Feb 2018 14:00:05 GM"); //your code }
区别在于Expires
设置的是过期时间点,且该过期时间点参考的是本地时间,本地时间会因为机器故障、人为修改等原因不尽相同。而Cache-Control
设置的则是过期时间段
另外,如果同时设置了Cache-Control
和Expires
,Expires
会被覆盖、会被忽略
ETag
MD5
全称:MD5信息摘要算法
英文:Message Digest Algorithm MD5
作用:比较两个文件的差异
原理:一个文件通过几个步骤将产生出一个128位(16字节)的散列值(hash value)
特点:两个文件,差异越小,算出来的MD5值差别越大
使用:专门生成MD5的软件,npm安装。。。
ETag
若需要启用ETag
设置,服务器要设置ETag响应头,该响应头将服务器端的页面的MD5值返回给浏览器。这样,浏览器在下次请求的时候,会多提交一个请求头if-none-match
,里面存放即是服务器响应回来的MD5值。如此,服务器能够对比自己现在的MD5和浏览器发送过来的MD5,如果一样就不用返回服务器端页面了,如果不一样才将服务器端的新页面返回给浏览器
ETag示例
前端代码:
$.post('/sign_in', hash) .then((response)=>{ window.location.href = '/index.html' }, (request)=>{ alert('邮箱与密码不匹配') })
使用Node.js写的后端:
if(path === '/sign_in' && method === 'post'){ //your code let string = fs.readFileSync('./sign_in.html','utf-8'); let fileMD5 = md5(string); let lastMD5 = request.headers['if-none-match']; if(fileMD5 === lastMD5){ response.statusCode = 304; }else{ response.setHeader("ETag",fileMD5); response.write(string); } //your code }
来来来,看这里:
- 第一,浏览器的第一个请求是没有
if-none-match
这个请求的,所以,服务器会设置一个响应头response.setHeader("ETag",fileMD5);
,将服务器端页面的MD5返回给浏览器 - 第二,等浏览器再次发送请求的时候,就会多带上一个请求头
if-none-match
- 第三,看到后端代码,取到浏览器请求过来的MD5
let lastMD5 = request.headers['if-none-match'];
,得到服务器端文件现在的MD5let fileMD5 = md5(string);
(已经用npm安装过生成MD5的程序),如果相同,返回304if(fileMD5 === lastMD5){response.statusCode = 304;}
,如果不同,返回新的MD5和新的页面else{response.setHeader("ETag",fileMD5);response.write(string);}
Last-Modify
Last-Modify
和ETag
作用一样,用法也一样,唯一不同的地方是ETag
返回的是一个MD5值,而Last-Modify
返回的是一个时间点。也就是说,ETag
对比浏览器和服务器页面的MD5,Last-Modify
对比浏览器和服务器页面的时间点。
如果更新时间间隔较短,请选用ETag
,更新时间中等,可以选用Last-Modify
。当然,ETag
优先级是要高于Last-Modify
的。另外,Lsat-Modify
不支持秒级别更新。这一段请参考:https://www.zhihu.com/questio...
小结
关于http
缓存有下面几种控制:
Cache-Control
:使用Max-Age
设定缓存过期时间段Expires
:直接设定缓存过期时间点ETag
:对比两端文件的MD5值Last-Modify
:对比两端文件的最后修改时间点