1.浏览器缓存
强缓存
第一次请求后,服务器会把过期时间通过响应头中的Expires和Cache-Control(优先考虑)字段告诉浏览器,通过这两个字段,浏览器在后面的请求发送前,会去检查这两个字段,判断该资源是否过期,如果没有过期则直接使用,否则发送请求。
(1)Expires
Expires即过期时间,存在于服务器返回的响应头中,告诉浏览器在这个日期之前可以直接使用该资源。
//表示资源在2019年11月22日8点41分过期,过期了就得向服务器发请求
Expires: Wed, 22 Nov 2019 08:41:00 GMT
缺点:服务器的时间可能和浏览器的时间不一致,所以这个过期时间可能是不准确的,因此这种方式在HTTP1.1版本中被抛弃了。
(2)Cache-Control
基于Expires的缺点,HTTP1.1版本提出了Cache-Control。Cache-Control通过里面的max-age字段来控制缓存的过期时间。
Cache-Control常见属性
- max-age:缓存过期时长,单位为秒
- max-stale:(保鲜时长,个人比喻说法)假设其值为100,则表示:缓存过期后的100秒内,依然可以拿来使用
- max-fresh:与上一字段相反,缓存到期时间还剩100秒就不能使用了,不新鲜了。
- no-cache:不能使用强缓存,直接进入协商缓存
- no-store:不使用缓存
- public:可以被任何终端缓存,包括代理服务器,CDN
- private:只能被浏览器终端缓存
- s-maxage:优先级高于max-age,适用于共享缓存(如CDN)
协商缓存
当强缓存失效后,浏览器发送请求,并在请求头中携带相应的缓存tag
,这样的tag分为两种:If-Modified-Since和If-None-Match,服务器接受后,根据其具体数值来决定是否使用缓存
(1)If-Modified-Since
第一次请求资源时,服务器会在响应头中返回Last-Modified字段,浏览器接收后,在以后的请求中,会在请求头中添加If-Modified-Since字段,服务器接收后,会通过对比自身的Last-Modified,来决定是否使用缓存
如果是一样的,就说明文件没有被更新过,则返回状态码304和空响应体
如果不一样,说明资源更新了,则返回对应资源,并在响应头中携带新的Last-Modified字段
(2)If-None-Match
同理,第一次请求后,服务器会在响应头中返回Etag字段,该字段是当前资源文件的唯一标识符,只要文件发生变化,都会重新生成。
浏览器再次请求时,在请求头中携带If-None-Match字段,服务器通过对比该字段与自身的Etag字段,来判断是否使用缓存。
2.缓存位置
前面我们提到,当强缓存命中或者服务器端返回304后我们直接从缓存中获取资源,那么这些资源在哪里呢?
浏览器的缓存位置一共有四种,按优先级从高到底排列分别是:
- Service Worker
- Memory Cache
- Disk Cache
- Push Cache
3.浏览器本地存储:Cookie,localStorage和sessionStorage
(1)Cookie
HTTP协议是一个无状态的协议,Cookie最开始被设计出来其实并不是来做本地存储的,而是为了弥补HTTP在状态管理上的不足。内部通过键值对来进行存储。
相关字段:
- Path:可以访问该Cookie的路径
- httpOnly:表示禁止通过JS访问Cookie,减少XSS攻击
- Secure:只能在https请求中才能携带
- SameSite:规定浏览器不能在跨域请求中携带Cookie,减少CSRF攻击
- Domain:域名,跨域或者Cookie的白名单
- Expires/Max-size:过期时间
缺点:
- 容量缺陷:Cookie的体积上限只有4kb
- 性能缺陷:Cookie紧跟域名,不管域名下面某一个地址需不需要这个Cookie,请求后悔携带上完整的Cookie,造成性能浪费
- 安全缺陷:Cookie以纯文本的形式在浏览器和服务器中进行传输,很容易被非法用户截获
(2)localStorage
localStorage有一点跟Cookie一样,就是针对同一个域名,会存储相同的一段localStorage
特点:
- 容量:localStorage的容量上限为5M
- 只存在客户端,默认不参与与服务端的通信
- 存储方式:以键值对的方式存储,并且都只能是字符串形式,因此一些其它数据类型需要通过
JSON.stringify进行转换,再通过setItem进行存储,以及通过getItem进行获取,removeItem进行删除操作
let obj = { name: 'chj', age: '18' };
//存储
localStorage.setItem('info', JSON.stringify(obj));
//获取
let me = JSON.parse(localStorage.getItem('info'));
//删除
localStorage.removeItem('info');
(3)sessionStorage
这个和上面的localStorage差不多,唯一的区别就是,sessionStorage的存储是会话级别的,也就是说,页面关闭后,sessionStorage就不存在了,下次打开里面不会存有上次保存的内容
4.从输入URL到页面呈现发生了什么--网络请求
(1)构建请求
浏览器进程接收到用户输入的URL请求,通过进程间通信(IPC)把URL发送给网络进程
(2)查找强缓存
网络进程接收到后,先检查强缓存,如果命中,直接使用缓存,否则进入下一步
(3)DNS解析
这里DNS的查找如下:操作系统首先会查找hosts文件中是否有记录,有的话将相应的IP直接返回,否则去本地dns解析器找,看有没有缓存,如果没有就去找计算机上配置的dns服务器,还没有的话,就去根DNS服务器(全球13台,固定IP地址)查找。
(4)建立TCP连接
接着,发送请求建立连接,同时Chrome有个机制,同一个域名下同时最多只能建立6个TCP连接,超过的需要进入排队等待状态。
TCP连接经历:
- 通过三次握手建立客户端与服务器端的连接
- 进行数据传输
- 通过四次挥手断开连接
(5)发送HTTP请求
TCP连接建立完毕后,客户端就可以和服务端开始通信,发送HTTP请求,HTTP请求携带:请求行,请求头和请求体。
(6)网络响应
HTTP请求到达服务器后,服务器进行处理,最后把数据传给浏览器,也就是网络响应,具体有三个部分:响应行,响应头和响应体
此时是否要断开连接,由请求头或者响应头的Connection字段来决定,如果其包含Connection: Keep-Alive,表示建立了持久连接,则不会马上断开。
(7)渲染
当浏览器接收到响应后,如果响应头中的Content-Type为text/html,那么接下来就要进入浏览器的解析和渲染工作了。
5.从输入URL到页面呈现发生了什么--解析渲染
当浏览器接收到响应后,如果响应头中的Content-Type为text/html,那么接下来就要进入浏览器的解析和渲染工作了。
- 第一步是处理HTML标记并构造DOM树,解析器进行解析过程中,发现非阻塞资源,例如一张图片,浏览器会请求这些资源,并且继续解析;当遇到一个CSS文件时,解析也可以继续进行。但是,对于
<script>标签(特别是没有async或着defer属性)会阻塞渲染并停止HTML的解析。 - 第二步就是构造CSSOM树,在解析CSS文件时,并不会阻塞DOM树的构建,但是由于一些
- 然后,将DOM树和CSSOM组合成一个Render树,计算样式树或渲染树从DOM树的根开始构建,遍历每个可见节点。
- 接着,在渲染树上运行布局以计算每个节点的几何体,布局是确定呈现树中所有节点宽度、高度和位置,以及确定页面上每个对象的大小和位置的过程。
- 最后,将每个节点绘制到屏幕上。
6.浏览器的回流和重绘
回流:
当我们对DOM的宽度、高度、位置等等几何尺寸进行修改时,浏览器需要重新计算元素的几何属性,此时其它元素的几何属性和位置也会因此受到影响,然后再将计算的结果绘制出来,这个过程叫做回流。
重绘:
当我们对DOM的修改导致了样式的变化,但并没有影响几何属性(比如修改颜色)时,浏览器并不需要重新计算元素的几何属性,只需要为改变的元素绘制新的样式,这个过程叫做重绘。
由此我们可以看出,重绘不一定导致回流,但回流一定导致重绘。
我们如何避免呢?
CSS
- 避免使用CSS表达式(例如:calc())
- 将动画效果应用到
position属性为absolute或者fixed的元素上
JavaScript
- 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class,并一次性更改class属性
- 避免频繁操作DOM,创建一个
documentFragment,再它上面应用所有DOM操作,最后再把它添加到文档中。
7.浏览器跨域
其实,总结一下,常见的就三种:
-
jsonp:
原理:利用
<script>标签没有跨域限制的漏洞,网页可以得到从其它来源产生的JSON数据特点:兼容性好
缺点:仅支持get方法,具有局限性,不安全,容易遭受XSS攻击
-
CORS:
通过设置服务器端的
Access-Control-Allow-Origin字段开启CORS,来进行跨域 -
nginx:
原理:搭建一个中转服务器,由于跨域问题只是存在于浏览器,因此只需要通过nginx配置一个代理服务器(域名与浏览器相同,再反向代理访问服务器端)
8.浏览器安全
XSS攻击
XSS全称是Cross Site Scripting(跨站脚本),为了和CSS区分,于是命名为XSS。XSS攻击是指浏览器执行恶意脚本,从而拿到用户的信息并进行操作。
XSS攻击主要有三种方式:
存储型
就是将恶意脚本存储了,并且一般都是存储在了服务器端的数据库,然后在客户端执行这些脚本,从而达到攻击的目的。
常见的场景就是留言评论区提交一段脚本代码,如果前后端没有做转义工作,那么这些内容将存储到数据库,在页面渲染时会直接执行,这种攻击造成的影响是比较大的。
反射型
反射型XSS攻击指的是将恶意脚本作为网络请求的一部分。
比如:
http://chj.com?q=<script>alert("哈哈哈")</script>
这样,在服务器端会拿到q参数,然后将内容返回给浏览器端,浏览器将这些内容作为HTML的一部分,发现是一个脚本,直接执行,就被攻击了。
不过,这并不会存储这些恶意脚本,相对来说影响较小。
文档型
文档型的XSS攻击并不会经过服务器,而是作为中间人的角色,在数据传输过程种劫持网络数据包,修改里面的HTML文件。
预防措施:
- 对用户输入的内容进行转码或者过滤,让其不可执行
- 利用Cookie的HttpOnly属性,防止窃取Cookie
CSRF攻击
CSRF(Cross-Site Request Forgery),跨站请求伪造,指的是黑客诱导用户点击链接,进入第三方网站,然后利用用户目前的登录状态发起跨站请求。
发起CSRF攻击有三个必要条件:
- 目标网站一定有CSRF漏洞
- 用户登录过目标网站,并且浏览器保存了登录状态
- 需要用户主动打开第三方站点
主要三种攻击类型:
-
自动发GET类型:比如img标签,当用户打开这个网站会自动发起带Cookie 的资源请求
<img src="恶意网址"> -
自动发POST类型:比如一个隐藏的表单,在用户进入页面的时候会自动提交表单
<form id="hack" action="恶意网址" methods="post"> ... </form> <srcript> document.getElementById('hack').submit(); </script> -
诱导链接型:就是诱导用户主动点击链接
防范措施
-
在Cookie种添加SameSite属性,其中有三个值:
strict:严格模式,严禁第三方请求携带Cookie
lax:这种模式比较宽松,只能在get方法提交表单或者a标签发送get请求的情况下才可以携带Cookie
None:默认模式,请求会自动携带上Cookie
-
Token验证:
当登录成功后,服务器除了返回Cookie,还会返回一个token,在后面的Cookie验证过程中,都需要携带这个token进行验证