前端面试题手册:网络基础
HTTP
HTTP 协议简介
- HTTP 协议(HyperText Transfer Protocol,超文本传输协议)是因特网上应用最为广泛的一种网络传输协议,所有的 WWW 文件都必须遵守这个标准。
- HTTP 是基于 TCP/IP 通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)
- HTTP 协议通常承载于 TCP 协议之上,有时也承载于 TLS 或 SSL 协议层之上,这个时候,就成了我们常说的 HTTPS。
- HTTP 是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP 是一个无状态的协议。
- HTTP 默认的端口号为 80,HTTPS 的端口号为 443。
HTTPS 和 HTTP 的区别
- HTTPS 协议需要到 CA(证书颁发机构)申请证书,一般免费证书很少,需要交费。
- HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,HTTPS 运行在 SSL/TLS 之上,SSL/TLS 运行在 TCP 之上,所有传输的内容都经过加密的。
- HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
- HTTP 的连接很简单,是无状态的;HTTPS 协议是由 HTTP+SSL 协议构建的可进行加密传输、身份认证的网络协议,可以有效的防止运营商劫持,解决了防劫持的一个大问题,比 HTTP 协议安全。
说到 HTTP,不得不提的就是 TCP/IP 网络模型,一般是五层模型。
TCP/IP 协议族
但是也可以分为四层,就是把链路层和物理层都表示为网络接口层
应用层 (application layer)
- 直接为应用进程提供服务。应用层协议定义的是应用进程间通讯和交互的规则,不同的应用有着不同的应用层协议,如 HTTP 协议(万维网服务)、FTP 协议(文件传输)、SMTP 协议(电子邮件)、DNS(域名查询)等。
传输层 (transport layer) 有时也译为运输层,它负责为两台主机中的进程提供通信服务。该层主要有以下两种协议:
- 传输控制协议 (Transmission Control Protocol,TCP):提供面向连接的、可靠的数据传输服务,数据传输的基本单位是报文段(segment);
- 用户数据报协议 (User Datagram Protocol,UDP):提供无连接的、尽最大努力的数据传输服务,但不保证数据传输的可靠性,数据传输的基本单位是用户数据报。
网络层 (internet layer)
- 有时也译为网际层,它负责为两台主机提供通信服务,并通过选择合适的路由将数据传递到目标主机。
数据链路层 (data link layer)
- 负责将网络层交下来的 IP 数据报封装成帧,并在链路的两个相邻节点间传送帧,每一帧都包含数据和必要的控制信息(如同步信息、地址信息、差错控制等)。
物理层 (physical Layer)
- 确保数据可以在各种物理媒介上进行传输,为数据的传输提供可靠的环境。
还有一种就是 OSI 七层网络模型,它就是在五层协议之上加了表示层和会话层
OSI 七层模型
(1)应用层 OSI 参考模型中最靠近用户的一层,是为计算机用户提供应用接口,也为用户直接提供各种网络服务。我们常见应用层的网络服务协议有:HTTP,HTTPS,FTP,POP3、SMTP 等。
- 在客户端与服务器中经常会有数据的请求,这个时候就是会用到 http(hyper text transfer protocol)(超文本传输协议)或者 https.在后端设计数据接口时,我们常常使用到这个协议。
- FTP 是文件传输协议,在开发过程中,个人并没有涉及到,但是我想,在一些资源网站,比如百度网盘、迅雷应该是基于此协议的。
- SMTP 是 simple mail transfer protocol(简单邮件传输协议)。在一个项目中,在用户邮箱验证码登录的功能时,使用到了这个协议。
(2)表示层
-
表示层提供各种用于应用层数据的编码和转换功能,确保一个系统的应用层发送的数据能被另一个系统的应用层识别。如果必要,该层可提供一种标准表示形式,用于将计算机内部的多种数据格式转换成通信中采用的标准表示形式。数据压缩和加密也是表示层可提供的转换功能之一。
-
在项目开发中,为了方便数据传输,可以使用 base64 对数据进行编解码。如果按功能来划分,base64 应该是工作在表示层。
(3)会话层
- 会话层就是负责建立、管理和终止表示层实体之间的通信会话。该层的通信由不同设备中的应用程序之间的服务请求和响应组成。
(4)传输层
- 传输层建立了主机端到端的链接,传输层的作用是为上层协议提供端到端的可靠和透明的数据传输服务,包括处理差错控制和流量控制等问题。该层向高层屏蔽了下层数据通信的细节,使高层用户看到的只是在两个传输实体间的一条主机到主机的、可由用户控制和设定的、可靠的数据通路。我们通常说的,TCP UDP 就是在这一层。端口号既是这里的“端”。
(5)网络层
- 本层通过 IP 寻址来建立两个节点之间的连接,为源端的运输层送来的分组,选择合适的路由和交换节点,正确无误地按照地址传送给目的端的运输层。就是通常说的 IP 层。这一层就是我们经常说的 IP 协议层。IP 协议是 Internet 的基础。我们可以这样理解,网络层规定了数据包的传输路线,而传输层则规定了数据包的传输方式。
(6)数据链路层
- 将比特组合成字节,再将字节组合成帧,使用链路层地址 (以太网使用 MAC 地址)来访问介质,并进行差错检测。 网络层与数据链路层的对比,通过上面的描述,我们或许可以这样理解,网络层是规划了数据包的传输路线,而数据链路层就是传输路线。不过,在数据链路层上还增加了差错控制的功能。
(7)物理层
- 实际最终信号的传输是通过物理层实现的。通过物理介质传输比特流。规定了电平、速度和电缆针脚。常用设备有(各种物理设备)集线器、中继器、调制解调器、网线、双绞线、同轴电缆。这些都是物理层的传输介质。
- OSI 七层模型通信特点:对等通信 对等通信,为了使数据分组从源传送到目的地,源端 OSI 模型的每一层都必须与目的端的对等层进行通信,这种通信方式称为对等层通信。在每一层通信过程中,使用本层自己协议进行通信。
HTTP 报文
- 用于 HTTP 协议交互的信息被称为 HTTP 报文。客户端的 HTTP 报文叫请求报文,服务端的 HTTP 报文叫响应报文。
请求报文
是由请求行(请求方法、协议版本)、请求首部(请求 URI、客户端信息等)和内容实体(用户信息和资源信息等,可为空)构成。
响应报文
是由状态行(协议版本、状态码)、响应首部(服务器名称、资源标识等)和内容实体(服务端返回的资源信息)构成。
常见的请求头
- Accept:浏览器能够处理的内容类型
- Accept-Charset:浏览器能够显示的字符集
- Accept-Encoding:浏览器能够处理的压缩编码
- Accept-Language:浏览器当前设置的语言
- Connection:浏览器与服务器之间连接的类型
- Cookie:当前页面设置的任何 Cookie
- Host:发出请求的页面所在的域
- Referer:发出请求的页面的 URL
- User-Agent:浏览器的用户代理字符串
常见的响应头
- Date:表示消息发送的时间,时间的描述格式由 rfc822 定义
- server:服务器名称
- Connection:浏览器与服务器之间连接的类型
- Cache-Control:控制 HTTP 缓存
- content-type:表示后面的文档属于什么 MIME 类型
请求方法
- GET:get 方法一般用于获取服务器资源
- POST:post 方法一般用于传输实体主体
- PUT:put 方法一般用于传输文件
- DELETE:delete 方法用于删除文件
- HEAD:head 方法用于获取报文首部,不返回报文主体
- OPTIONS:向服务器发送该方***返回对指定资源所支持的 HTTP 请求方法。
Get 和 Post 的区别
- get 方法一般用于请求,比如你在浏览器地址栏输入
www.baidu.com
其实就是发送了一个 get 请求,它的主要特征是请求服务器返回资源,而 post 方法一般用于 - get 方法是不安全的,因为你在发送请求的过程中,你的请求参数会拼在 URL 后面,从而导致容易被攻击者窃取,对你的信息造成破坏和伪造;
/test/demo_form.asp?name1=value1&name2=value2
而 post 方法是把参数放在请求体 body 中的,这对用户来说不可见。
POST /test/demo_form.asp HTTP/1.1
Host: w3schools.com
name1=value1&name2=value2
- get 请求的 URL 有长度限制,而 post 请求会把参数和值放在消息体中,对数据长度没有要求。
- get 请求会被浏览器主动 cache,而 post 不会,除非手动设置。
- get 请求在浏览器反复的 回退/前进 操作是无害的,而 post 操作会再次提交表单请求。
- get 请求在发送过程中会产生一个 TCP 数据包;post 在发送过程中会产生两个 TCP 数据包。对于 get 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200(返回数据);而对于 post,浏览器先发送 header,服务器响应 100 continue,浏览器再发送 data,服务器响应 200 ok(返回数据)。
什么是无状态协议,HTTP 是无状态协议吗,怎么解决
- 我们知道,假如某个特定的客户机在短时间内两次请求同一个对象,服务器并不会因为刚刚为该用户提供了该对象就不再做出反应,而是重新发送该对象,就像该服务器已经完全忘记不久之前所做过的是一样。因为一个 HTTP 服务器并不保存关于客户机的任何信息,所以我们说 HTTP 是一个无状态协议。
通常有两种解决方案:
① 基于 Session 实现的会话保持 在客户端第一次向服务器发送 HTTP 请求后,服务器会创建一个 Session 对象并将客户端的身份信息以键值对的形式存储下来,然后分配一个会话标识(SessionId)给客户端,这个会话标识一般保存在客户端 Cookie 中,之后每次该浏览器发送 HTTP 请求都会带上 Cookie 中的 SessionId 到服务器,服务器根据会话标识就可以将之前的状态信息与会话联系起来,从而实现会话保持。
- 优点:安全性高,因为状态信息保存在服务器端。
- 缺点:由于大型网站往往采用的是分布式服务器,浏览器发送的 HTTP 请求一般要先通过负载均衡器才能到达具体的后台服务器,倘若同一个浏览器两次 HTTP 请求分别落在不同的服务器上时,基于 Session 的方法就不能实现会话保持了。 【解决方法:采用中间件,例如 Redis,我们通过将 Session 的信息存储在 Redis 中,使得每个服务器都可以访问到之前的状态信息】
② 基于 Cookie 实现的会话保持 当服务器发送响应消息时,在 HTTP 响应头中设置 Set-Cookie 字段,用来存储客户端的状态信息。客户端解析出 HTTP 响应头中的字段信息,并根据其生命周期创建不同的 Cookie,这样一来每次浏览器发送 HTTP 请求的时候都会带上 Cookie 字段,从而实现状态保持。基于 Cookie 的会话保持与基于 Session 实现的会话保持最主要的区别是前者完全将会话状态信息存储在浏览器 Cookie 中。
- 优点:服务器不用保存状态信息, 减轻服务器存储压力,同时便于服务端做水平拓展。
- 缺点:该方式不够安全,因为状态信息存储在客户端,这意味着不能在会话中保存机密数据。除此之外,浏览器每次发起 HTTP 请求时都需要发送额外的 Cookie 到服务器端,会占用更多带宽。
拓展:Cookie 被禁用了怎么办? 若遇到 Cookie 被禁用的情况,则可以通过重写 URL 的方式将会话标识放在 URL 的参数里,也可以实现会话保持。
HTTP1.0 HTTP 1.1 主要区别
- 长连接 HTTP 1.0 需要使用 keep-alive 参数来告知服务器端要建立一个长连接,而 HTTP1.1 默认支持长连接。 HTTP 是基于 TCP/IP 协议的,创建一个 TCP 连接是需要经过三次握手的,有一定的开销,如果每次通讯都要重新建立连接的话,对性能有影响。因此最好能维持一个长连接,可以用个长连接来发多个请求。
- 节约带宽 HTTP 1.1 支持只发送 header 信息(不带任何 body 信息),如果服务器认为客户端有权限请求服务器,则返回 100,否则返回 401。客户端如果接受到 100,才开始把请求 body 发送到服务器。 这样当服务器返回 401 的时候,客户端就可以不用发送请求 body 了,节约了带宽。 另外 HTTP 还支持传送内容的一部分。这样当客户端已经有一部分的资源后,只需要跟服务器请求另外的部分资源即可。这是支持文件断点续传的基础。
- HOST 域 现在可以 web server 例如 tomat,设置虚拟站点是非常常见的,也即是说,web server 上的多个虚拟站点可以共享同一个 ip 和端口。 HTTP1.0 是没有 host 域的,HTTP1.1 才支持这个参数。
HTTP1.1 HTTP 2.0 主要区别
-
HTTP2 是一个二进制协议,HTTP1 是超文本协议,传输的内容都不是一样的
-
HTTP2 报头压缩,可以使用 HPACK 进行头部压缩,HTTP1 则不论什么请求都会发送
-
HTTP2 服务端推送(Server push),允许服务器预先将网页所需要的资源 push 到浏览器的内存当中
-
HTTP2 遵循多路复用,代替同一域名下的内容,只建立一次连接,HTTP1.x 不是,对域名有 6~8 个连接限制
-
HTTP2 引入二进制数据帧和流的概念,其中帧对数据进行顺序标识,这样浏览器收到数据之后,就可以按照序列对数据进行合并,而不会出现合并后数据错乱的情况,同样是因为有了序列,服务器就可以并行的传输数据,这就是流所做的事情。HTTP2 对同一域名下所有请求都是基于流的,也就是说同一域名下不管访问多少文件,只建立一次连接。
https 协议的工作原理
- 客户端在使用 HTTPS 方式与 Web 服务器通信时有以下几个步骤:
- 客户使用 https url 访问服务器,则要求 web 服务器建立 ssl 链接。
- web 服务器接收到客户端的请求之后,会将网站的证书(证书中包含了公钥),返回或者说传输给客户端。
- 客户端和 web 服务器端开始协商 SSL 链接的安全等级,也就是加密等级。
- 客户端浏览器通过双方协商一致的安全等级,建立会话密钥,然后通过网站的公钥来加密会话密钥,并传送给网站。
- web 服务器通过自己的私钥解密出会话密钥。
- web 服务器通过会话密钥加密与客户端之间的通信。
HTTP 状态码
HTTP 状态码由三个十进制数字组成,第一个数字定义了状态码的类型,后两个并没有起到分类的作用。 HTTP 状态码共有 5 种类型:
分类 | 分类描述 |
---|---|
1XX | 指示信息——表示请求正在处理 |
2XX | 成功——表示请求已被成功处理完毕 |
3XX | 重定向——要完成的请求需要进行附加操作 |
4XX | 客户端错误——请求有语法错误或者请求无法实现,服务器无法处理请求 |
5XX | 服务器端错误——服务器处理请求出现错误 |
相应的 HTTP 状态码列表:
状态码 | 英文名称 | 中文描述 |
---|---|---|
100 | ContinueContinu | 继续。客户端继续处理请求 |
200 | OK | 请求成功。请求所希望的响应头或数据体将随此响应返回 |
201 | Created | 请求以实现。并且有一个新的资源已经依据需求而建立 |
202 | Accepted | 请求已接受。已经接受请求,但还未处理完成 |
301 | Moved Permanently | 永久移动。请求的资源已被永久地移动到新 URI,返回信息会包含新的 URI,浏览器会自动定向到新 URI |
302 | Found | 临时移动。与 301 类似。但资源只是临时被移动,客户端应继续使用原有 URI |
304 | Not Modified | 未修改。如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码 |
400 | Bad Request | 客户端请求的语法错误,服务器无法理解;请求的参数有误 |
403 | Forbidden | 服务器已经理解请求,但是拒绝执行它 |
404 | Not Found | 请求失败,请求所希望得到的资源未被在服务器上发现 |
409 | Conflict | 由于和被请求的资源的当前状态之间存在冲突,请求无法完成 |
500 | Internal Server | 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理 |
502 | Bad Gateway | 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到无效的响应 |
503 | Service Unavailable | 由于临时的服务器维护或者过载,服务器当前无法处理请求,一段时间后可能恢复正常 |
HTTP 返回码中 301 与 302 的区别
301,302 都是 HTTP 状态的编码,都代表着某个 URL 发生了转移,不同之处在于:
- 301 redirect: 301 代表永久性转移(Permanently Moved)。
- 302 redirect: 302 代表暂时性转移(Temporarily Moved )。
字面上的区别就是 301 是永久重定向,而 302 是临时重定向。 当然,他们之间也是有共同点的,就是用户都可以看到 url 替换为了一个新的,然后发出请求。
- 301 比较常用的场景是使用域名跳转。比如,我们访问 http://www.baidu.com 会跳转到 https://www.baidu.com ,发送请求之后,就会返回 301 状态码,然后返回一个 location,提示新的地址,浏览器就会拿着这个新的地址去访问。
注意:301 请求是可以缓存的, 即通过看 status code,可以发现后面写着 from cache。或者你把你的网页的名称从 php 修改为了 html,这个过程中,也会发生永久重定向。
- 302 用来做临时跳转,比如未登陆的用户访问用户中心重定向到登录页面。访问 404 页面会重新定向到首页。
总结:
- 302 重定向只是暂时的重定向,搜索引擎会抓取新的内容而保留旧的地址,因为服务器返回 302,所以,搜索搜索引擎认为新的网址是暂时的。 而 301 重定向是永久的重定向,搜索引擎在抓取新的内容的同时也将旧的网址替换为了重定向之后的网址。
DNS
DNS 解析
1.首先会查询 hosts 文件是否有对应的网址映射关系。如果有,优先采取。
2.查找本地 DNS 解析器缓存, 如果有,取本地缓存。
3.如果 hosts 与本地 DNS 解析器缓存都没有相应的网址映射关系,首先会找 TCP/ip 参数中设置的首选 DNS 服务器,在此我们叫它本地 DNS 服务器,此服务器收到查询时,如果要查询的域名,包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析,此解析具有权威性。
4.如果要查询的域名,不由本地 DNS 服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个 IP 地址映射,完成域名解析,此解析不具有权威性。
5.如果上面都失败,则查看是否采用了转发模式,如果未用转发模式,往上一级域名,重复上面的动作,进行查询,直至找到 www . qq .com 主机。
6.如果用的是转发模式,此 DNS 服务器就会把请求转发至上一级 DNS 服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根 DNS 或把转请求转至上上级,以此循环。不管是本地 DNS 服务器用是是转发,还是根提示,最后都是把结果返回给本地 DNS 服务器,由此 DNS 服务器再返回给客户机。
浏览器内部机制、通信及缓存
浏览器的渲染过程
- 首先解析收到的文档,根据文档定义构建一棵 DOM 树,DOM 树是由 DOM 元素及属性节点组成的。
- 然后对 CSS 进行解析,生成 CSSOM 规则树。
- 根据 DOM 树和 CSSOM 规则树构建渲染树。渲染树的节点被称为渲染对象,渲染对象是一个包含有颜色和大小等属性的矩形,渲染对象和 DOM 元素相对应,但这种对应关系不是一对一的,不可见的 DOM 元素不会被插入渲染树。还有一些 DOM 元素对应几个可见对象,它们一般是一些具有复杂结构的元素,无法用一个矩形来描述。
- 当渲染对象被创建并添加到树中,它们并没有位置和大小,所以当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流)。这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。
- 布局阶段结束后是绘制阶段,遍历渲染树并调用渲染对象的 paint 方法将它们的内容显示在屏幕上,绘制使用 UI 基础组件。
浏览器内核
浏览器内核主要分成两部分:
- 渲染引擎的职责就是渲染,即在浏览器窗口中显示所请求的内容。默认情况下,渲染引擎可以显示 html、xml 文档及图片,它也可以借助插件显示其他类型数据,例如使用 PDF 阅读器插件,可以显示 PDF 格式。
- JS 引擎:解析和执行 javascript 来实现网页的动态效果。 最开始渲染引擎和 JS 引擎并没有区分的很明确,后来 JS 引擎越来越独立,内核就倾向于只指渲染引擎。
浏览器名称 | 内核 | 补充 |
---|---|---|
IE | Trident | 主要包含在 window 操作系统的 IE 浏览器中 |
firefox | Gecko | Gecko 的特点是代码完全公开,因此,其可开发程度很高 |
Safari | webkit | 苹果公司自己的内核,包含 WebCore 排版引擎及 JavaScriptCore 解析引擎 |
chrome | Chromium/Blink/webkit | Blink 是开源引擎 WebKit 中 WebCore 组件的一个分支 |
浏览器回流与重绘
一句话:回流必将引起重绘,重绘不一定会引起回流。
回流 (Reflow)
- 页面首次渲染
- 浏览器窗口大小发生改变
- 元素尺寸或位置发生改变
- 元素内容变化(文字数量或图片大小等等)
- 元素字体大小变化
- 添加或者删除可见的 DOM 元素
- 激活 CSS 伪类(例如::hover)
- 查询某些属性或调用某些方法
重绘 (Repaint)
- 当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility 等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
如何避免重绘重排
CSS
- CSS避免使用 table 布局。
- 尽可能在 DOM 树的最末端改变 class。
- 避免设置多层内联样式。
- 将动画效果应用到 position 属性为 absolute 或 fixed 的元素上。
- 避免使用 CSS 表达式(例如:calc())。
JavaScript
- 避免频繁操作样式,最好一次性重写 style 属性,或者将样式列表定义为 class 并一次性更改 class 属性。
- 避免频繁操作 DOM,创建一个 documentFragment,在它上面应用所有 DOM 操作,最后再把它添加到文档中。
- 也可以先为元素设置 display: none,操作结束后再把它显示出来。因为在 display 属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘。
- 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
浏览器缓存
一般来说浏览器缓存可以分为两类:
- 强缓存
- 协商缓存(对比缓存)
强缓存
浏览器在加载资源时,会先根据本地缓存资源的 header 中的信息判断是否命中强缓存,如果命中则直接使用缓存中的资源不会再向服务器发送请求。 对于强制缓存,服务器响应的 header 中会用两个字段来表明——Expires和Cache-Control
- Expires Exprires 的值为服务端返回的数据到期时间。当再次请求时的请求时间小于返回的此时间,则直接使用缓存数据。但由于服务端时间和客户端时间可能有误差,这也将导致缓存命中的误差,另一方面,Expires 是 HTTP1.0 的产物,故现在大多数使用 Cache-Control 替代。
同在 Response Headers 中 同为控制缓存的过期时间(早期使用) 如果 cache-control 与 expires 同时存在的话,cache-control 的优先级高于 expires
- Cache-Control Cache-Control 有很多属性,不同的属性代表的意义也不同。 private:客户端可以缓存 public:客户端和代理服务器都可以缓存 max-age=t:缓存内容将在 t 秒后失效 no-cache:需要使用协商缓存来验证缓存数据 no-store:所有内容都不会缓存。
协商缓存
流程图
当强缓存没有命中的时候,浏览器会发送一个请求到服务器,服务器根据请求头中的部分信息来判断是否命中缓存。如果命中,则返回 304,告诉浏览器资源未更新,可使用本地的缓存。对于协商缓存来说,缓存标识我们需要着重理解一下,下面我们将着重介绍它的两种缓存方案。
-
Last-Modified
-
假设此时我们的协商缓存用 Last-Modified 来判断。当浏览器第一次发送请求时,服务器返回资源并返回一个 Last-Modified 的值给浏览器。这个 Last-Modified 的值给到浏览器之后,浏览器会通过 If-Modified-Since 的字段来保存 Last-Modified 的值,且 If-Modified-Since 保存在请求头当中。
-
之后当浏览器再次发送请求时,请求头会带着 If-Modified-Since 的值去找服务器,服务器此刻就会匹配浏览器发过来的 If-Modified-Since 是否和自己最后一次修改的 Last-Modified 的值相等。如果相等,则返回 304,表示资源未被修改;如果不相等,则返回 200,并返回资源和新的 Last-Modified 的值。
-
-
Etag
- 假设此时我们的协商缓存用 Etag 来判断。当浏览器第一次发送请求时,服务器返回资源并返回一个 Etag 的值给浏览器。这个 Etag 的值给到浏览器之后,浏览器会通过 If-None-Match 的字段来保存 Etag 的值,且 If-None-Match 保存在请求头当中。
- 之后当浏览器再次发送请求时,请求头会带着 If-None-Match 的值去找服务器,服务器此刻就会匹配浏览器发过来的 If-None-Match 是否和自己最后一次修改的 Etag 的值相等。如果相等,则返回 304,表示资源未被修改;如果不相等,则返回 200,并返回资源和新的 Etag 的值。
拓展:
- Last-Modified 资源的最后修改时间,对应请求头为 If-Modified-Since;
- Etag 资源的唯一标识,所谓唯一,可以想象成时人类的指纹,具有唯一性;但 Etag 的本质是一个字符串;对应请求头为 If-None-Match。
- 当响应头部 Response Headers 同时存在 Last-Modified 和 Etag 的值时,会优先使用 Etag;
- Last-Modified 只能精确到秒级;
- 如果资源被重复生成,而内容不变,则 Etag 更精确。
总结 当浏览器再次访问一个已经访问过的资源时,它会这样做: 看看是否命中强缓存,如果命中,就直接使用缓存了; 如果没有命中强缓存,就发请求到服务器检查是否命中协商缓存; 如果命中协商缓存,服务器会返回 304 告诉浏览器使用本地缓存; 否则,返回 200,并返回资源。
刷新操作方式,对缓存的影响
(1)正常操作 定义: 地址栏输入 url,跳转链接,前进后退等。 对缓存的影响: 强制缓存有效,协商缓存有效。
(2)手动刷新 定义:F5,点击刷新按钮,右击菜单刷新。 对缓存的影响: 强制缓存失效,协商缓存有效。
(3)强制刷新 定义:ctrl + F5。 对缓存的影响: 强制缓存失效,协商缓存失效。
Cookie
Cookie 是服务器发送到客户端并保存在本地的一小块数据,它会在客户端下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。cookie 中的数据以{key:value}的形式存在。
cookie 机制原理
比如服务端要想记录用户的状态,就使用 response 向浏览器发送一个 Cookie。客户端浏览器会将这个 cookie 保存起来。浏览器再次请求服务端时,浏览器会把这个 cookie 带上。服务端检查这个 cookie 来获取用户状态。
cookie 有哪些字段可以设置
-
name 字段为一个 cookie 的名称。
-
value 字段为一个 cookie 的值。
-
domain 字段为可以访问此 cookie 的域名。
-
非顶级域名,如二级域名或者三级域名,设置的 cookie 的 domain 只能为顶级域名或者 二级域名或者三级域名本身,不能设置其他二级域名的 cookie,否则 cookie 无法生成。 顶级域名只能设置 domain 为顶级域名,不能设置为二级域名或者三级域名,否则 cookie 无法生成。
-
二级域名能读取设置了 domain 为顶级域名或者自身的 cookie,不能读取其他二级域名 domain 的 cookie。所以要想 cookie 在多个二级域名中共享,需要设置 domain 为顶级域名,这样就可以在所有二级域名里面或者到这个 cookie 的值了。
-
顶级域名只能获取到 domain 设置为顶级域名的 cookie,其他 domain 设置为二级域名的 无法获取。
-
-
path 字段为可以访问此 cookie 的页面路径。 比如 domain 是 abc.com,path 是/test,那么只有/test 路径下的页面可以读取此 cookie。
-
expires/Max-Age 字段为此 cookie 超时时间。若设置其值为一个时间,那么当到达此时间 后,此 cookie 失效。不设置的话默认值是 Session,意思是 cookie 会和 session 一起失效。 当浏览器关闭(不是浏览器标签页,而是整个浏览器) 后,此 cookie 失效。
-
Size 字段 此 cookie 大小。
-
http 字段 cookie 的 http only 属性。若此属性为 true,则只有在 http 请求头中会带有此 cookie 的信息,而不能通过 document.cookie 来访问此 cookie。
-
secure 字段 设置是否只能通过 https 来传递此条 cookie
优点:极高的扩展性和可用性 1.通过加密和安全传输技术(SSL),减少 cookie 被破解的可能性。 2.只在 cookie 中存放不敏感数据,即使被盗也不会有重大损失。 3.控制 cookie 的生命期,使之不会永远有效。偷盗者很可能拿到一个过期的 cookie。 缺点: 1.
Cookie
数量和长度的限制。每个 domain 最多只能有 20 条 cookie,每个 cookie 长度不能超过 4KB,否则会被截掉。 2.安全性问题。如果 cookie 被人拦截了,那人就可以取得所有的 session 信息。即使加密也与事无补,因为拦截者并不需要知道 cookie 的意义,他只要原样转发 cookie 就可以达到目的
创建 Cookie
Cookie cookie = new Cookie("cookieSessionId","qwertyuiop"); //构造函数
cookie.setDomain(".baidu.com"); // 设置域名
cookie.setPath("/"); // 设置路径
cookie.setMaxAge(Integer.MAX_VALUE); // 设置有效期为永久
response.addCookie(cookie); // 回写到客户端
Cookie 更新与删除
- Cookie 本身并没有提供修改和删除的方法,一般通过使用相同 name 的 Cookie 来覆盖原来的 Cookie,以达到更新或删除的目的。
//服务端从客户端取得 cookie
Cookie[] cookies = request.getCookies();
//服务端向客户端发送 cookie
response.addCookie(cookie);
Session
- Session 代表着服务器和客户端一次会话的过程。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当客户端关闭会话,或者 Session 超时失效时会话结束。
Session 机制原理:
- 当客户端请求创建一个 session 时,服务端会先检查客户端的请求里面有没有带着 session 标识-sessionId。如果有,则说明服务器以前已为此客户端创建过 session,于是就根据这个 sessionId 把 session 检索出来。如果客户端请求中不包含 sessionId,则为客户端创建一个 session 并且生成一个与这个 session 相关联的 sessionId。 这个 sessionId 将被在本次响应中返回给客户端保存。保存 sessionId 的方式大多情况下用的是 cookie。
创建 session
HttpSession session = request.getSession();
描述 cookie,session 的差异
- 作用范围不同,Cookie 保存在客户端(浏览器),Session 保存在服务器端。
- 有效期不同,Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭或者 Session 超时都会失效。
- 隐私策略不同,Cookie 存储在客户端,比较容易遭到不法获取,早期有人将用户的登录名和密码存储在 Cookie 中导致信息被窃取;Session 存储在服务端,安全性相对 Cookie 要好一些。
- 存储大小不同, 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie。
- session 不能区分路径,同一个用户在访问一个网站期间,所有的 session 在任何一个地方都可以访问到。Cookie 有个 setPath 的方法,可以设置可访问的路径,那么同一个网站中不同路径下的 cookie 互相是访问不到的
拓展:避免 localStorage 被更改 让 localStorage 禁止修改值
// 当 localStorage 值被修改的时候清除他并且跳转到登录页
window.addEventListener('storage', function () {
localStorage.clear();
window.location.replace("/#/login");
});
// 让 localStorage 值无法修改
window.addEventListener('storage', function () {
localStorage.setItem(e.key, e.oldValue)
});
三次握手
- SYN:同步序列编号。是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立 TCP 连接时,首先会发送的一个信号。客户端在接受到 SYN 消息时,就会在自己的段内生成一个随机值 X。
- SYN-ACK:服务器收到 SYN 后,打开客户端连接,发送一个 SYN-ACK 作为答复。确认号设置为比接收到的序列号多一个,即 X + 1,服务器为数据包选择的序列号是另一个随机数 Y。
- ACK:确认字符,表示发来的数据已确认接收无误。最后,客户端将 ACK 发送给服务器。序列号被设置为所接收的确认值即 Y + 1。
为什么要三次握手
客户端和服务端通信前要进行连接,“3 次握手”的作用就是双方都能明确自己和对方的收、发能力是正常的。
- 第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
- 第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。
- 第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力,服务端的发送、接收能力是正常的。
三次握手的主要目的是确认自己和对方的发送和接收都是正常的,从而保证了双方能够进行可靠通信。若采用两次握手,当第二次握手后就建立连接的话,此时客户端知道服务器能够正常接收到自己发送的数据,而服务器并不知道客户端是否能够收到自己发送的数据。 我们知道网络往往是非理想状态的(存在丢包和延迟),当客户端发起创建连接的请求时,如果服务器直接创建了这个连接并返回包含 SYN、ACK 和 Seq 等内容的数据包给客户端,这个数据包因为网络传输的原因丢失了,丢失之后客户端就一直接收不到返回的数据包。由于客户端可能设置了一个超时时间,一段时间后就关闭了连接建立的请求,再重新发起新的请求,而服务器端是不知道的,如果没有第三次握手告诉服务器客户端能否收到服务器传输的数据的话,服务器端的端口就会一直开着,等到客户端因超时重新发出请求时,服务器就会重新开启一个端口连接。长此以往, 这样的端口越来越多,就会造成服务器开销的浪费。
四次挥手
- 首先,客户端应用程序决定要终止连接(这里服务端也可以选择断开连接)。这会使客户端将 FIN 发送到服务器,并进入 FIN_WAIT_1 状态。当客户端处于 FIN_WAIT_1 状态时,它会等待来自服务器的 ACK 响应。
- 然后第二步,当服务器收到 FIN 消息时,服务器会立刻向客户端发送 ACK 确认消息。
- 当客户端收到服务器发送的 ACK 响应后,客户端就进入 FIN_WAIT_2 状态,然后等待来自服务器的 FIN 消息
- 服务器发送 ACK 确认消息后,一段时间(可以进行关闭后)会发送 FIN 消息给客户端,告知客户端可以进行关闭。
- 当客户端收到从服务端发送的 FIN 消息时,客户端就会由 FIN_WAIT_2 状态变为 TIME_WAIT 状态。处于 TIME_WAIT 状态的客户端允许重新发送 ACK 到服务器为了防止信息丢失。客户端在 TIME_WAIT 状态下花费的时间取决于它的实现,在等待一段时间后,连接关闭,客户端上所有的资源(包括端口号和缓冲区数据)都被释放。
为什么要四次挥手?
-
关闭连接时,当收到对方的 FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方是否现在关闭发送数据通道,需要上层应用来决定,因此,己方 ACK 和 FIN 一般都会分开发送。
-
为什么不是两次?
- 两次情况客户端说完结束就立马断开不再接收,无法确认服务端是否接收到断开消息,但且服务端可能还有消息未发送完。
-
为什么不是三次?
- 3 次情况服务端接收到断开消息,向客户端发送确认接受消息,客户端未给最后确认断开的回复。
-
举个例子:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B 回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。
TCP 和 UDP
区别和应用场景
区别
- TCP 是面向连接的,udp 是无连接的即发送数据前不需要先建立链接。
- TCP 提供可靠的服务。也就是说,通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP 尽最大努力交付,即不保证可靠交付。 并且因为 tcp 可靠,面向连接,不会丢失数据因此适合大数据量的交换。
- TCP 是面向字节流,UDP 面向报文,并且网络出现拥塞不会使得发送速率降低(因此会出现丢包,对实时的应用比如 IP 电话和视频会议等)。
- TCP 只能是 1 对 1 的,UDP 支持 1 对 1,1 对多。
- TCP 的首部较大为 20 字节,而 UDP 只有 8 字节。
- TCP 是面向连接的可靠性传输,而 UDP 是不可靠的。
应用场景
TCP 粘包及处理
粘包是指为了防止数据量过小导致大量传输,全将多个 TCP 段合并成一个发送。就是将若干个包数据粘成一个包,从接收缓冲区看,后一包数据的头紧接送前一包数据的尾。因为 TCP 层传输是流式传输,流,最大的问题是没有边界,没有边界就会造成数据粘在一起。 拆包就是将任务拆分处理了,降低出错率。
-
造成粘包的场景
- 接收方不及时接收缓冲区的包,造成多个包接收
- 因为 TCP 默认使用 Nagle 算法,这个算法本身也可能会导致粘包问题
- 由于 TCP 的复用造成粘包。由于 TCP 连接的复用性,建立一条连接可以供一台主机上的多个进程使用,那么多种不同结构的数据到 TCP 的流式传输里,边界分割肯定会出现奇葩的问题
- 数据包过大造成的粘包问题,比如应用进程缓冲区的一条消息内容大小超过了发送缓存区的大小,就可能产生粘包,因为消息已经被分割了,前一部分已经被接收了,但另一部分可能刚放入缓存冲区准备发送,这样就会导致后一部分粘包
- 流量控制,拥塞控制也可能导致粘包
Nagle 算法,主要做两件事: 一是只有上一个分组得到确认,才会发送下一个分组; 二是收集多个小分组,数据包大小达到最大段大小(MSS),在一个确认到来时一起发送。 多个分组拼成一个数据段发出去,如果没有处理好边界问题,在解包的时候就会发生粘包
粘包怎么处理
-
如果是 Nagle 算法导致的,结合应用场景适当关闭算法就可以了
-
如果不是
- 尾部标记序列。通过特殊标识符表示数据包的边界,比如\n\r\t 或一些隐藏字符
- 头部标记分步接收。在 TCP 报文的头部加上表示数据长度。使用带消息头的协议,消息头存储开始标识及消息长度信息,服务商获取消息头的时候解析出消息长度,然后向后读取该长度的内容
- 应用层发送数据时定长发送,服务端读取既定长度的内容作为一条完整消息,如果不够长,就在空位上补固定字符
UDP 为什么不会粘包
- 因为 UDP 是面向消息的协议,UDP 段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据
- UDP 具有保护消息边界,在每个 UDP 包中有消息头(消息来源地址,端口信息等),这样对于接收端来说容易进行分区处理。传输协议把数据当作一条独立的消息在网上传输,接收方只能接收独立的消息,如果消息内容过大,超过接收方一次所能接受的大小,就会丢失一部分数据,因为就算是丢失,它也不会分两次去接收.
TCP 流量控制与拥塞控制
-
流量控制 所谓流量控制就是让发送方的发送速率不要太快,让接收方来得及接收。如果接收方来不及接收发送方发送的数据,那么就会有分组丢失。在 TCP 中利用可变长的滑动窗口机制可以很方便的在 TCP 连接上实现对发送方的流量控制。主要的方式是接收方返回的 ACK 中会包含自己的接收窗口大小,以控制发送方此次发送的数据量大小(发送窗口大小)。
-
拥塞控制 在实际的网络通信系统中,除了发送方和接收方外,还有路由器,交换机等复杂的网络传输线路,此时就需要拥塞控制。拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况。常用的解决方法有:慢开始和拥塞避免、快重传和快恢复。
-
拥塞控制和流量控制的区别 拥塞控制往往是一种全局的,防止过多的数据注入到网络之中,而 TCP 连接的端点只要不能收到对方的确认信息,猜想在网络中发生了拥塞,但并不知道发生在何处,因此,流量控制往往指点对点通信量的控制,是端到端的问题。
TCP 协议丢包后,如何解决丢包的问题
为了满足 TCP 协议不丢包。TCP 协议有如下规定:
- 数据分片:发送端对数据进行分片,接受端要对数据进行重组,由 TCP 确定分片的大小并控制分片和重组
- 到达确认:接收端接收到分片数据时,根据分片数据序号向发送端发送一个确认
- 超时重发:发送方在发送分片时设置超时定时器,如果在定时器超时之后没有收到相应的确认,重发分片数据
- 滑动窗口:TCP 连接的每一方的接受缓冲空间大小固定,接收端只允许另一端发送接收端缓冲区所能接纳的数据,TCP 在滑动窗口的基础上提供流量控制,防止较快主机致使较慢主机的缓冲区溢出
- 失序处理:作为 IP 数据报来传输的 TCP 分片到达时可能会失序,TCP 将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层;
- 重复处理:作为 IP 数据报来传输的 TCP 分片会发生重复,TCP 的接收端必须丢弃重复的数据;
- 数据校验:TCP 将保持它首部和数据的检验和,这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到分片的检验或有差错,TCP 将丢弃这个分片,并不确认收到此报文段导致对端超时并重发
UDP 如何保证可靠的传输
最简单的方式是在应用层模仿传输层 TCP 的可靠性传输。下面不考虑拥塞处理,可靠 UDP 的简单设计。 1、添加 seq/ack 机制,确保数据发送到对端 2、添加发送和接收缓冲区,主要是用户超时重传。 3、添加超时重传机制。
详细说明:
- 发送端发送数据时,生成一个随机 seq=x,然后每一片按照数据大小分配 seq。
- 数据到达接收端后接收端放入缓存,并发送一个 ack=x 的包,表示对方已经收到了数据。
- 发送端收到了 ack 包后,删除缓冲区对应的数据。
- 时间到后,发送端定时任务检查是否需要重传数据。
进程与线程的区别
- 性质不同:进程是资源分配的基本单位,线程是 cpu 执行运算和调度的基本单位
- 归属不同:一个操作系统中可以有很多进程,一个进程可以有很多线程
- 开销不同:进程创建、销毁和切换的开销都要远大于线程
- 拥有资源不同:每个进程都拥有自己的内存和资源,一个进程中的线程会共享这些内存和资源
- 通信方式不同:进程之间可以通过管道、消息队列、共享内存、信号量,以及 Socket 等机制实现通信,线程之间主要通过共享变量及其变种形式实现通信
- cpu 利用率不同:进程的 cpu 利用率低,因为需要额外的上下文切换开销;线程的 cpu 利用率高,因为切换简单
- 可靠性不同:进程的可靠性要高于线程
进程之间的通信方式
(1)管道通信 管道是一种最基本的进程间通信机制。管道就是操作系统在内核中开辟的一段缓冲区,进程 1 可以将需要交互的数据拷贝到这段缓冲区,进程 2 就可以读取了。 管道的特点:
- 只能单向通信
- 只能血缘关系的进程进行通信
- 依赖于文件系统
- 生命周期随进程
- 面向字节流的服务
- 管道内部提供了同步机制
(2)消息队列通信 消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。 使用消息队列进行进程间通信,可能会收到数据块最大长度的限制约束等,这也是这种通信方式的缺点。如果频繁的发生进程间的通信行为,那么进程需要频繁地读取队列中的数据到内存,相当于间接地从一个进程拷贝到另一个进程,这需要花费时间。
(3)信号量通信 共享内存最大的问题就是多进程竞争内存的问题,就像类似于线程安全问题。我们可以使用信号量来解决这个问题。信号量的本质就是一个计数器,用来实现进程之间的互斥与同步。例如信号量的初始值是 1,然后 a 进程来访问内存 1 的时候,我们就把信号量的值设为 0,然后进程 b 也要来访问内存 1 的时候,看到信号量的值为 0 就知道已经有进程在访问内存 1 了,这个时候进程 b 就会访问不了内存 1。所以说,信号量也是进程之间的一种通信方式。
(4)信号通信 信号(Signals )是 Unix 系统中使用的最古老的进程间通信的方法之一。操作系统通过信号来通知进程系统中发生了某种预先规定好的事件(一组事件中的一个),它也是用户进程之间通信和同步的一种原始机制。
(5)共享内存通信 共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问(使多个进程可以访问同一块内存空间)。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
(6)套接字通信 上面说的共享内存、管道、信号量、消息队列,他们都是多个进程在一台主机之间的通信,那两个相隔几千里的进程能够进行通信吗?答是必须的,这个时候 Socket 这家伙就派上用场了,例如我们平时通过浏览器发起一个 http 请求,然后服务器给你返回对应的数据,这种就是采用 Socket 的通信方式了。
如何实现浏览器内多个标签页之间的通信?
实现多个标签页之间的通信,本质上都是通过中介者模式来实现的。因为标签页之间没有办法直接通信,因此我们可以找一个中介者,让标签页和中介者进行通信,然后让这个中介者来进行消息的转发。通信方法如下:
- 使用 websocket 协议,因为 websocket 协议可以实现服务器推送,所以服务器就可以用来当做这个中介者。标签页通过向服务器发送数据,然后由服务器向其他标签页推送转发。
- 使用 ShareWorker 的方式,shareWorker 会在页面存在的生命周期内创建一个唯一的线程,并且开启多个页面也只会使用同一个线程。这个时候共享线程就可以充当中介者的角色。标签页间通过共享一个线程,然后通过这个共享的线程来实现数据的交换。
- 使用 localStorage 的方式,我们可以在一个标签页对 localStorage 的变化事件进行监听,然后当另一个标签页修改数据的时候,我们就可以通过这个监听事件来获取到数据。这个时候 localStorage 对象就是充当的中介者的角色。
// 添加
localStorage.setItem(key,value)
// 删除
localStorage.removeItem(key,value)
// 添加监听 storage 的变化
window.onload = function () {
window.addEventListener('storage', function (event) {
// event 事件对象包含 domain newValue oldValue key
console.log(event.key + '=' + event.newValue);
})
}
- 使用 postMessage 方法,如果我们能够获得对应标签页的引用,就可以使用 postMessage 方法,进行通信。
怎么看网站的性能如何
检测页面加载时间一般有两种方式,一种是被动去测:就是在被检测的页面置入脚本或 探针,当用户访问网页时,探针自动采集数据并传回数据库进行分析,另一种主动监测 的方式,即主动的搭建分布式受控环境,模拟用户发起页面访问请求,主动采集性能数 据并分析,在检测的精准度上,专业的第三方工具效果更佳,比如说性能极客
浏览器安全
HTTPS 是如何保证安全的?
先理解两个概念:
- 对称加密:即通信的双⽅都使⽤同⼀个秘钥进⾏加解密,对称加密虽然很简单性能也好,但是⽆法解决⾸次把秘钥发给对⽅的问题,很容易被⿊客拦截秘钥。
- ⾮对称加密: 私钥 + 公钥= 密钥对 即⽤私钥加密的数据,只有对应的公钥才能解密,⽤公钥加密的数据,只有对应的私钥才能解密 因为通信双⽅的⼿⾥都有⼀套⾃⼰的密钥对,通信之前双⽅会先把⾃⼰的公钥都先发给对⽅ 然后对⽅再拿着这个公钥来加密数据响应给对⽅,等到到了对⽅那⾥,对⽅再⽤⾃⼰的私钥进⾏解密
⾮对称加密虽然安全性更⾼,但是带来的问题就是速度很慢,影响性能。
解决⽅案: 结合两种加密⽅式,将对称加密的密钥使⽤⾮对称加密的公钥进⾏加密,然后发送出去,接收⽅使⽤私钥进⾏解密得到对称加密的密钥,然后双⽅可以使⽤对称加密来进⾏沟通。
此时⼜带来⼀个问题,中间⼈问题: 如果此时在客户端和服务器之间存在⼀个中间⼈,这个中间⼈只需要把原本双⽅通信互发的公钥,换成⾃⼰的公钥,这样中间⼈就可以轻松解密通信双⽅所发送的所有数据。
所以这个时候需要⼀个安全的第三⽅颁发证书(CA),证明身份的身份,防⽌被中间⼈攻击。 证书中包括:签发者、证书⽤途、使⽤者公钥、使⽤者私钥、使⽤者的 HASH 算法、证书到期时间等。
但是问题来了,如果中间⼈篡改了证书,那么身份证明是不是就⽆效了?这个证明就⽩买了,这个时候需要⼀个新的技术,数字签名。 数字签名就是⽤ CA ⾃带的 HASH 算法对证书的内容进⾏ HASH 得到⼀个摘要,再⽤ CA 的私钥加密,最终组成数字签名。当别⼈把他的证书发过来的时候,我再⽤同样的 Hash 算法,再次⽣成消息摘要,然后⽤ CA 的公钥对数字签名解密,得到 CA 创建的消息摘要,两者⼀⽐,就知道中间有没有被⼈篡改了。这个时候就能最⼤程度保证通信的安全了。
简述 HTTPS 中间人攻击
中间人攻击过程如下:
- 服务器向客户端发送公钥。
- 攻击者截获公钥,保留在自己手上。
- 然后攻击者自己生成一个【伪造的】公钥,发给客户端。
- 客户端收到伪造的公钥后,生成加密 hash 值发给服务器。
- 攻击者获得加密 hash 值,用自己的私钥解密获得真秘钥。
- 同时生成假的加密 hash 值,发给服务器。
- 服务器用私钥解密获得假秘钥。
- 服务器用假秘钥加密传输信息
防范方法:
- 服务端在发送浏览器的公钥中加入 CA 证书,浏览器可以验证 CA 证书的有效性
WEB 安全防范
XSS 攻击
XSS 攻击指的是跨站脚本攻击,是一种代码注入攻击。攻击者通过在网站注入恶意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如 cookie 等。
XSS 的本质是因为网站没有对恶意代码进行过滤,与正常的代码混合在一起了,浏览器没有办法分辨哪些脚本是可信的,从而导致了恶意代码的执行。
攻击者可以通过这种攻击方式可以进行以下操作:
- 获取页面的数据,如 DOM、cookie、localStorage;
- DOS 攻击,发送合理请求,占用服务器资源,从而使用户无法访问服务器;
- 破坏页面结构;
- 流量劫持(将链接指向某网站);
攻击类型 XSS 可以分为存储型、反射型和 DOM 型:
- 存储型指的是恶意脚本会存储在目标服务器上,当浏览器请求数据时,脚本从服务器传回并执行。
- 反射型指的是攻击者诱导用户访问一个带有恶意代码的 URL 后,服务器端接收数据后处理,然后把带有恶意代码的数据发送到浏览器端,浏览器端解析这段带有 XSS 代码的数据后当做脚本执行,最终完成 XSS 攻击。
- DOM 型指的通过修改页面的 DOM 节点形成的 XSS。
防御 XSS 攻击
- 可以从浏览器的执行来进行预防,一种是使用纯前端的方式,不用服务器端拼接后返回(不使用服务端渲染)。另一种是对需要插入到 HTML 中的代码做好充分的转义。对于 DOM 型的攻击,主要是前端脚本的不可靠而造成的,对于数据获取渲染和字符串拼接的时候应该对可能出现的恶意代码情况进行判断。
- 使用 CSP,CSP 的本质是建立一个白名单,告诉浏览器哪些外部资源可以加载和执行,从而防止恶意代码的注入攻击。
CSP 指的是内容安全策略,它的本质是建立一个白名单,告诉浏览器哪些外部资源可以加载和执行。我们只需要配置规则,如何拦截由浏览器自己来实现。 通常有两种方式来开启 CSP,一种是设置 HTTP 首部中的 Content-Security-Policy,一种是设置 meta 标签的方式
- 对一些敏感信息进行保护,比如 cookie 使用 http-only,使得脚本无法获取。也可以使用验证码,避免脚本伪装成用户执行一些操作。
CSRF 攻击
CSRF 攻击指的是跨站请求伪造攻击,攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求。如果用户在被攻击网站中保存了登录状态,那么攻击者就可以利用这个登录状态,绕过后台的用户验证,冒充用户向服务器执行一些操作。 CSRF 攻击的本质是利用 cookie 会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。
常见的 CSRF 攻击有三种:
- GET 类型的 CSRF 攻击,比如在网站中的一个 img 标签里构建一个请求,当用户打开这个网站的时候就会自动发起提交。
- POST 类型的 CSRF 攻击,比如构建一个表单,然后隐藏它,当用户进入页面时,自动提交这个表单。
- 链接类型的 CSRF 攻击,比如在 a 标签的 href 属性里构建一个请求,然后诱导用户去点击。
防御 CSRF 攻击
-
进行同源检测,服务器根据 http 请求头中 origin 或者 referer 信息来判断请求是否为允许访问的站点,从而对请求进行过滤。当 origin 或者 referer 信息都不存在的时候,直接阻止请求。这种方式的缺点是有些情况下 referer 可以被伪造,同时还会把搜索引擎的链接也给屏蔽了。所以一般网站会允许搜索引擎的页面请求,但是相应的页面请求这种请求方式也可能被攻击者给利用。(Referer 字段会告诉服务器该网页是从哪个页面链接过来的)
-
使用 CSRF Token 进行验证 服务器向用户返回一个随机数 Token,当网站再次发起请求时,在请求参数中加入服务器端返回的 token,然后服务器对这个 token 进行验证。这种方法解决了使用 cookie 单一验证方式时,可能会被冒用的问题,但是这种方法存在一个缺点就是,我们需要给网站中的所有请求都添加上这个 token,操作比较繁琐。还有一个问题是一般不会只有一台网站服务器,如果请求经过负载平衡转移到了其他的服务器,但是这个服务器的 session 中没有保留这个 token 的话,就没有办法验证了。这种情况可以通过改变 token 的构建方式来解决。
网络劫持有哪几种,如何防范?
⽹络劫持分为两种: (1)DNS 劫持: (输⼊京东被强制跳转到淘宝这就属于 dns 劫持)
- DNS 强制解析: 通过修改运营商的本地 DNS 记录,来引导⽤户流量到缓存服务器
- 302 跳转的⽅式: 通过监控⽹络出⼝的流量,分析判断哪些内容是可以进⾏劫持处理的,再对劫持的内存发起 302 跳转的回复,引导⽤户获取内容
(2)HTTP 劫持: (访问⾕歌但是⼀直有贪玩蓝⽉的⼴告),由于 http 明⽂传输,运营商会修改你的 http 响应内容(即加⼴告)
DNS 劫持由于涉嫌违法,已经被监管起来,现在很少会有 DNS 劫持,⽽ http 劫持依然⾮常盛⾏,最有效的办法就是全站 HTTPS,将 HTTP 加密,这使得运营商⽆法获取明⽂,就⽆法劫持你的响应内容。
非对称加密和对称加密
对称加密
- 对称加密是指用来加密和解密的是同一个秘钥。其特点是加密速度快,但是秘钥容易被黑客截获,所以安全性不高。常见的有 AES、DES 算法。
非对称加密
- 非对称加密是指用来加密和解密的是不同的秘钥,它们是成对出现的,称为公钥和私钥,知道其中一个秘钥是无法推导出另外一个秘钥的。用公钥加密的内容需要用私钥才能解密,用私钥加密的内容需要用公钥才能解密。非对称加密的特点是安全性高,缺点是加密速度慢。常见的有 RSA 算法。
CDN
- 内容分发网络,它是构建在现有互联网基础之上的一层智能虚拟网络,它通过在不同地理位置建立节点服务器,对源网站资源进行不同节点的缓存,让用户在访问资源时可以就近获取所需的资源内容。CDN 服务可以大大缩减用户访问服务器所需时间,提高了访问效率和响`应速度与网站的可用性,解决了网络带宽小,用户访问量大,网点分布不均等问题。
- CDN 工作原理
①当用户点击网站页面上的内容URL,经过本地DNS系统解析,DNS系统会最终将域名的解析权交给CNAME指向的CDN专用DNS服务器。
②CDN的DNS服务器将CDN的全局负载均衡设备IP地址返回用户。
③用户向CDN的全局负载均衡设备发起内容URL访问请求。
④CDN全局负载均衡设备根据用户IP地址,以及用户请求的内容URL,选择一台用户所属区域的区域负载均衡设备,告诉用户向这台设备发起请求。
⑤区域负载均衡设备会为用户选择一台合适的缓存服务器提供服务,选择的依据包括:根据用户IP地址,判断哪一台服务器距用户最近;根据用户所请求的URL中携带的内容名称,判断哪一台服务器上有用户所需内容;查询各个服务器当前的负载情况,判断哪一台服务器尚有服务能力。基于以上这些条件的综合分析之后,区域负载均衡设备会向全局负载均衡设备返回一台缓存服务器的IP地址。
⑥全局负载均衡设备把服务器的IP地址返回给用户。
⑦用户向缓存服务器发起请求,缓存服务器响应用户请求,将用户所需内容传送到用户终端。如果这台缓存服务器上并没有用户想要的内容,而区域均衡设备依然将它分配给了用户,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器将内容拉到本地。 DNS 服务器根据用户 IP 地址,将域名解析成相应节点的缓存服务器 IP 地址,实现用户就近访问。使用 CDN 服务的网站,只需将其域名解析权交给 CDN 的 GSLB 设备,将需要分发的内容注入 CDN,就可以实现内容加速了。
经典问题:从输入 URL 到页面加载完成的过程
-
在浏览器地址栏输入 URL
-
首先判断是否有永久重定向(301)
-
浏览器查看资源是否有强缓存,若命中强缓存就直接从缓存中使用,若协商缓存则需要到服务器进行校验资源是否可用。
- HTTP1.0 提供 expires,值为一个绝对时间表示缓存过期时间
- HTTP1.1 增加了一个 cache-control:max-age,值为以秒为单位的最大缓存过期时间。还有 no-cache,no-store,分别表示可以缓存但会立即失效和不能缓存。
-
浏览器解析 URL 获取协议,主机,端口,path。
-
浏览器组装一个 HTTP(GET)请求报文。
-
DNS 解析,查找过程如下:
- 首先查找浏览器缓存;
- 第二查找本机缓存
- 第三查找 hosts 文件
- 第四路由器缓存
- 第五 ISP DNS 缓存
- 第六 DNS 查询(递归查询、迭代查询)
-
端口建立 TCP 连接,三次握手:
- 客户端发送一个 TCP 的 SYN=1,seq=x 的连接请求到服务器端。
- 服务器端返回 SYN=1,ACK=x+1,Seq=Y 的确认连接字段。
- 客户端收到确认连接字段后发生 ACK=Y+1,seq=z
-
TCP 连接建立后发起 HTTP 请求
-
服务器接收到请求并解析,将请求转发到服务程序。
-
服务器检查 HTTP 请求头部是否有缓存验证信息,如果缓存未过期,返回 304 状态码
-
处理程序读取完整请求并准备 HTTP 响应,同时可能需要查询数据库等操作
-
服务器将相应报文通过 TCP 连接发送会浏览器。
-
浏览器接收到 HTTP 响应,根据情况选择是否关闭 TCP 连接,若关闭的话四次挥手。
- 浏览器端首先向服务器端发送数据已发送完毕的报文
- 服务器端接收到之后,返回确认报文同意浏览器停止发送数据,但此时服务器端依旧可以接收未接收完的数据,并且可以返回数据给浏览器端。
- 服务器端将所有数据返回给客户端之后,向客户端发送请求连接释放报文。
- 客户端收到连接释放报文后,向服务器端返回确认连接释放报文。
-
浏览器接收到服务端的响应报文后,会检查响应状态码,根据不同的状态码处理不同的情况。
-
如果资源可以缓存,进行缓存。
-
将响应内容进行解码。
-
根据资源类型决定如何处理。
-
若资源类型为 html 类型,解析 html 文档,构建 DOM 树,并下载相关资源,构造 CSSOM 树,执行 js 脚本。
-
首先构造 DOM 树
-
解析过程中如果遇到图片、样式表、js 文件便启动下载
-
构建 CSSOM 树
-
根据 DOM 树和 CSSOM 树构建渲染树:
- 从 DOM 树的根节点遍历所有可见节点。
- 对每一个节点找到恰当的 CSSOM 规则并应用
- 发布可视节点的内容和计算样式
-
js 脚本解析:
- 浏览器创建 document 对象并解析 html,将解析的文本和节点添加到文档中。
-
html 解析器遇到没有 defer 和 async 属性的 script 时,将他们添加到文档中,然后去执行脚本语句。在脚本下载和执行时 html 解析器会暂停。直到 script 下载和执行完毕
- 当解析器遇到 async 属性的 script 时,开始下载脚本但会继续解析文档,当脚本下载完毕时就会立刻回过头去执行该脚本,但是解析器不会停下来等它下载。
- 当解析器遇到 defer 属性的 script 时,defer 脚本会在文档解析完毕时按照顺序执行,并且可以访问完整文档。
- 浏览器在 document 对象上触发 DOMContentLoaded 事件
- 此时文档完全解析完成,浏览器可能还在等待图片等内容加载,等待这些内容完成载入并且所有异步脚本完成载入和执行之后,window 会触发 loaded 事件
-
显示页面(html 解析过程中会逐步显示页面)。