基础部分

一、HTTP协议的发展历程:

1.1989年,蒂姆·伯纳斯 - 李(Tim Berners-Lee)发表了一篇论文,提出了在互联网上构建超链接文档系统的构想。这篇论文中他确立了三项关键技术:

  • URI:即统一资源标识符,作为互联网上资源的唯一身份;
  • HTML:即超文本标记语言,描述超文本文档;
  • HTTP:即超文本传输协议,用来传输超文本。

2.HTTP/0.9 是个简单的文本协议,只能获取文本资源;

3.HTTP/1.0 确立了大部分现在使用的技术,但它不是正式标准;

  • 增加了 HEAD、POST 等新方法;
  • 增加了响应状态码,标记可能的错误原因;
  • 引入了协议版本号概念;
  • 引入了 HTTP Header(头部)的概念,让 HTTP 处理请求和响应更加灵活;
  • 传输的数据不再仅限于文本。

HTTP/1.0 中引入了请求头和响应头。在支持多种类型文件下载的基础之上,HTTP/1.0 还提供了 Cache 机制、用户代理、状态码等一些基础信息。

4.HTTP/1.1 是目前互联网上使用最广泛的协议,功能也非常完善;

  • 增加了 PUT、DELETE 等新的方法;
  • 增加了缓存管理和控制;
  • 明确了连接管理,允许持久连接;
  • 允许响应数据分块(chunked),利于传输大文件;
  • 强制要求 Host 头,让互联网主机托管成为可能。

HTTP/1.1,增加了持久连接方法来提升连接效率,同时还尝试使用管线化技术提升效率(不过由于各种原因,管线化技术最终被各大厂商放弃了)。除此之外,HTTP/1.1 还引入了 Cookie、虚拟主机的支持、对动态内容的支持等特性。

5.HTTP/2 基于 Google 的 SPDY 协议,注重性能改善,但还未普及;

  • 二进制协议,不再是纯文本;
  • 可发起多个请求,废弃了 1.1 里的管道;
  • 使用专用算法压缩头部,减少数据传输量;
  • 允许服务器主动向客户端推送数据;
  • 增强了安全性,“事实上”要求加密通信。

6.HTTP/3 基于 Google 的 QUIC 协议,是将来的发展方向。

二、HTTP是什么

  1. HTTP 是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范
  2. HTTP 专门用来在两点之间传输数据,不能用于广播、寻址或路由。
  3. HTTP 传输的是文字、图片、音频、视频等超文本数据。
  4. HTTP 是构建互联网的重要基础技术,它没有实体,依赖许多其他的技术来实现,但同时许多技术也都依赖于它。
    HTTP 通常跑在 TCP/IP 协议栈之上,依靠 IP 协议实现寻址和路由、TCP 协议实现可靠数据传输、DNS 协议实现域名查找、SSL/TLS 协议实现安全通信。此外,还有一些协议依赖于 HTTP,例如 WebSocket、HTTPDNS 等。这些协议相互交织,构成了一个协议网,而 HTTP 则处于中心地位。
    http是双向协议,一方为发起方,另一方为响应方。适用范围也不仅限于在服务器和浏览器之间,严格来讲,它是计算机与计算机之间的一种信息交换的约定规范。

三、与HTTP相关的各种概念

1.互联网的正式名称是 Internet,里面存储着无穷无尽的信息资源,我们通常所说的“上网”实际上访问的只是互联网的一个子集“万维网”(World Wide Web),它基于 HTTP 协议,传输 HTML 等超文本资源,能力也就被限制在 HTTP 协议之内。互联网上还有许多万维网之外的资源,例如常用的电子邮件、BT 和 Magnet 点对点下载、FTP 文件下载、SSH 安全登录、各种即时通信服务等等,它们需要用各自的专有协议来访问。

2.不过由于 HTTP 协议非常灵活、易于扩展,而且“超文本”的表述能力很强,所以很多其他原本不属于 HTTP 的资源也可以“包装”成 HTTP 来访问,这就是我们为什么能够总看到各种“网页应用”——例如“微信网页版”“邮箱网页版”——的原因。

1.互联网上绝大部分资源都使用 HTTP 协议传输;

2.浏览器是 HTTP 协议里的请求方,即 User Agent;

3.服务器是 HTTP 协议里的应答方,常用的有 Apache 和 Nginx;

4.CDN: 位于浏览器和服务器之间,主要起到缓存加速的作用;

“内容分发网络”。它应用了 HTTP 协议里的缓存和代理技术,代替源站响应客户端的请求。

CDN 有什么好处呢?简单来说,它可以缓存源站的数据,让浏览器的请求不用“千里迢迢”地到达源站服务器,直接在“半路”就可以获取响应。如果 CDN 的调度算法很优秀,更可以找到离用户最近的节点,大幅度缩短响应时间。

5.爬虫:爬虫是另一类 User Agent,是自动访问网络资源的程序。

爬虫也有不好的一面,它会过度消耗网络资源,占用服务器和带宽,影响网站对真实数据的分析,甚至导致敏感信息泄漏。所以,又出现了“反爬虫”技术,通过各种手段来限制爬虫。其中一项就是“君子协定”robots.txt,约定哪些该爬,哪些不该爬。无论是“爬虫”还是“反爬虫”,用到的基本技术都是两个,一个是 HTTP,另一个就是 HTML。

四、与HTTP相关的各种协议

五、网络分层

1.TCP/IP 网络分层模型(四层)

1.1第一层叫“链接层”(link layer)

负责在以太网、WiFi 这样的底层网络上发送原始数据包,工作在网卡这个层次,使用 MAC 地址来标记网络上的设备,所以有时候也叫 MAC 层。

1.2第二层叫“网际层”或者“网络互连层”(internet layer)

IP 协议就处在这一层。因为 IP 协议定义了“IP 地址”的概念,所以就可以在“链接层”的基础上,用 IP 地址取代 MAC 地址,把许许多多的局域网、广域网连接成一个虚拟的巨大网络,在这个网络里找设备时只要把 IP 地址再“翻译”成 MAC 地址就可以了。

1.3.第三层叫“传输层”(transport layer)

这个层次协议的职责是保证数据在 IP 地址标记的两点之间“可靠”地传输,是 TCP 协议工作的层次,另外还有它的一个“小伙伴”UDP。TCP 是一个有状态的协议,需要先与对方建立连接然后才能发送数据,而且保证数据不丢失不重复。而 UDP 则比较简单,它无状态,不用事先建立连接就可以任意发送数据,但不保证数据一定会发到对方。两个协议的另一个重要区别在于数据的形式。TCP 的数据是连续的“字节流”,有先后顺序,而 UDP 则是分散的小数据包,是顺序发,乱序收。关于 TCP 和 UDP 可以展开讨论的话题还有很多,比如最经典的“三次握手”和“四次挥手”,一时半会很难说完,好在与 HTTP 的关系不是太大,以后遇到了再详细讲解。

1.4协议栈的第四层叫“应用层”(application layer)

由于下面的三层把基础打得非常好,所以在这一层就“百花齐放”了,有各种面向具体应用的协议。例如 Telnet、SSH、FTP、SMTP 等等,当然还有我们的 HTTP。MAC 层的传输单位是帧(frame),IP 层的传输单位是包(packet),TCP 层的传输单位是段(segment),HTTP 的传输单位则是消息或报文(message)。但这些名词并没有什么本质的区分,可以统称为数据包。

2.OSI 网络分层模型

OSI 模型分成了七层,部分层次与 TCP/IP 很像,从下到上分别是:

http(s)协议 - 图1

第一层:物理层,网络的物理形式,例如电缆、光纤、网卡、集线器等等;

第二层:数据链路层,它基本相当于 TCP/IP 的链接层;

第三层:网络层,相当于 TCP/IP 里的网际层;

第四层:传输层,相当于 TCP/IP 里的传输层;

第五层:会话层,维护网络中的连接状态,即保持会话和同步;

第六层:表示层,把数据转换为合适、可理解的语法和语义;

第七层:应用层,面向具体的应用传输数据。

3.两个分层模型的映射关系

http(s)协议 - 图2

第一层:物理层,TCP/IP 里无对应;

第二层:数据链路层,对应 TCP/IP 的链接层;

第三层:网络层,对应 TCP/IP 的网际层;

第四层:传输层,对应 TCP/IP 的传输层;

第五、六、七层:统一对应到 TCP/IP 的应用层。

4.TCP/IP 协议栈的工作方式

http(s)协议 - 图3

HTTP 协议的传输过程就是这样通过协议栈逐层向下,每一层都添加本层的专有数据层层打包,然后通过下层发送出去。接收数据则是相反的操作,从下往上穿过协议栈,逐层拆包,每层去掉本层的专有头,上层就会拿到自己的数据。但下层的传输过程对于上层是完全“透明”的,上层也不需要关心下层的具体实现细节,所以就 HTTP 层次来看,它不管下层是不是 TCP/IP 协议,看到的只是一个可靠的传输链路,只要把数据加上自己的头,对方就能原样收到。

5.总结:

  • TCP/IP 分为四层,核心是二层的 IP 和三层的 TCP,HTTP 在第四层;
  • OSI 分为七层,基本对应 TCP/IP,TCP 在第四层,HTTP 在第七层;
  • OSI 可以映射到 TCP/IP,但这期间一、五、六层消失了;
  • 日常交流的时候我们通常使用 OSI 模型,用四层、七层等术语;
  • HTTP 利用 TCP/IP 协议栈逐层打包再拆包,实现了数据传输,但下面的细节并不可见。

有一个辨别四层和七层比较好的(但不是绝对的)小窍门,“两个凡是”:凡是由操作系统负责处理的就是四层或四层以下,否则,凡是需要由应用程序(也就是你自己写代码)负责处理的就是七层。

思考:

二层转发是什么?

三层路由是什么?

DNS 协议位于哪一层呢?

CDN 工作在那一层呢?

答案:

二层转发:设备工作在链路层,只认识MAC地址,所以建立MAC地址和端口的映射关系来决定往哪个端口转发。

三层路由:设备工作在网络层,有IP协议,通过解析数据包头信息,找到目标IP地址。

dns,网络请求的第一步是域名解析,所以工作在应用层,cdn,应用层

六、DNS域名解析

1.域名的形式

域名是一个有层次的结构,是一串用“.”分隔的多个单词,最右边的被称为“顶级域名”,然后是“二级域名”,层级关系向左依次降低。

最左边的是主机名,通常用来表明主机的用途,比如“www”表示提供万维网服务、“mail”表示提供邮件服务,不过这也不是绝对的,名字的关键是要让我们容易记忆。

2.域名的解析

2.1DNS核心系统

DNS 的核心系统是一个三层的树状、分布式服务,基本对应域名的结构:

  • 根域名服务器(Root DNS Server):管理顶级域名服务器,返回“com”“net”“cn”等顶级域名服务器的 IP 地址;
  • 顶级域名服务器(Top-level DNS Server):管理各自域名下的权威域名服务器,比如 com 顶级域名服务器可以返回 apple.com 域名服务器的 IP 地址;
  • 权威域名服务器(Authoritative DNS Server):管理自己域名下主机的 IP 地址,比如 apple.com 权威域名服务器可以返回 www.apple.com 的 IP 地址。

http(s)协议 - 图4

在这里根域名服务器是关键,它必须是众所周知的,否则下面的各级服务器就无从谈起了。

有了这个系统以后,任何一个域名都可以在这个树形结构里从顶至下进行查询,就好像是把域名从右到左顺序走了一遍,最终就获得了域名对应的 IP 地址。

例如,你要访问“www.apple.com”,就要进行下面的三次查询:

  • 访问根域名服务器,它会告诉你“com”顶级域名服务器的地址;
  • 访问“com”顶级域名服务器,它再告诉你“apple.com”域名服务器的地址;
  • 最后访问“apple.com”域名服务器,就得到了“www.apple.com”的地址。

2.2如何减轻域名解析的压力

缓存。

首先,许多大公司、网络运行商都会建立自己的 DNS 服务器,作为用户 DNS 查询的代理,代替用户访问核心 DNS 系统。这些“野生”服务器被称为“非权威域名服务器”,可以缓存之前的查询结果,如果已经有了记录,就无需再向根服务器发起查询,直接返回对应的 IP 地址。

其次,操作系统里也会对 DNS 解析结果做缓存,如果你之前访问过“www.apple.com”,那么下一次在浏览器里再输入这个网址的时候就不会再跑到 DNS 那里去问了,直接在操作系统里就可以拿到 IP 地址。

另外host文件,操作系统里还有一个特殊的“主机映射”文件,通常是一个可编辑的文本,在 Linux 里是“/etc/hosts”,在 Windows 里是“C:\WINDOWS\system32\drivers\etc\hosts”,如果操作系统在缓存里找不到 DNS 记录,就会找这个文件。

有了这些缓存,方便了用户,也减轻了各级 DNS 服务器的压力,效率就大大提升了。

3.基于域名的负载均衡

有两种实现方式:

第一种方式,因为域名解析可以返回多个 IP 地址,所以一个域名可以对应多台主机,客户端收到多个 IP 地址后,就可以自己使用轮询算法依次向服务器发起请求,实现负载均衡。

第二种方式,域名解析可以配置内部的策略,返回离客户端最近的主机,或者返回当前服务质量最好的主机,这样在 DNS 端把请求分发到不同的服务器,实现负载均衡。

4.思考:

在浏览器地址栏里随便输入一个不存在的域名,比如就叫“www. 不存在.com”,试着解释一下它的 DNS 解析过程。

如果因为某些原因,DNS 失效或者出错了,会出现什么后果

1:在浏览器地址栏里随便输入一个不存在的域名,比如就叫“www. 不存在.com”,试着解释一下它的 DNS 解析过程。

第一:拿着域名去浏览器缓存中查找,这里有些疑问,浏览器中缓存在哪里?过期时间大概多久?过期机制是什么?
第二:去操作系统缓存中去查找,同样的疑问,缓存在哪里?过期时间多久?过期机制是什么?怎么确认一下?
第三:去hosts文件中查找,这个文件还好进程修改,那么每个操作系统这个文件都必须在默认的路径下吗?否则就需要全部文件遍历一下啦?
第四:去非权威DNS服务器中查找,到此这些都可以认为是一种缓存思想的运用,把域名和IP的映射关系信息放在更快的存储设备之上,把这份信息放在离用户更近的地方,来加速用户通过域名获取IP的速度。
第五:去根域名DNS服务器中查找,管理顶级域名服务器,返回“com”“net”“cn”等顶级域名服务器的 IP 地址;注意,此时仅拿着根域名去查找,下面的查找过程有一定的层次结构,这样不仅查找的信息少存储的信息也会少,速度也会更快一些
第六:去顶级域名DNS服务器中查找,此时拿的信息是“com”,查找 “不存在.com” ,确实查不到,然后就抛出异常,逐级返回了,最后把找不到的提升信息给到用户——无法访问此网站,找不到www.不存在.com的服务器IP地址
第七:假设第六步找到了,则会继续去二级域名DNS服务器去查找,根据“不存在.com”找“ww.不存在.com”
第八:去权威DNS服务器中找到“ww.不存在.com”对应的IP地址
第九:然后返回,在返回的过程中会更新操作系统缓存,浏览器缓存,不知道非权威DNS服务器中的域名和IP映射信息是怎么进去的,是定时加载嘛?

综合:域名解析的过程

浏览器缓存 -> 操作系统缓存 -> hosts 文件 -> 本地域名服务器 -> 根域名服务器 -> 顶级域名服务器 -> 权威域名服务器
客户端向本地域名服务器获取,是递归查询
本地域名服务器向根域名服务器获取,可以是递归也可是迭代
递归就是你交给别人,让别人查到,在返回给你
迭代就是你找别人要,他叫你去别的地方找

2:如果因为某些原因,DNS 失效或者出错了,会出现什么后果?
会出现“无法访问此网站,找不到www.XXX.com的服务器IP地址”

七、输入网址按下回车键,页面发生了什么

  • HTTP 协议基于底层的 TCP/IP 协议,所以必须要用 IP 地址建立连接;
  • 如果不知道 IP 地址,就要用 DNS 协议去解析得到 IP 地址,否则就会连接失败;
  • 建立 TCP 连接后会顺序收发数据,请求方和应答方都必须依据 HTTP 规范构建和解析报文;
  • 为了减少响应时间,整个过程中的每一个环节都会有缓存,能够实现“短路”操作;
  • 虽然现实中的 HTTP 传输过程非常复杂,但理论上仍然可以简化成实验里的“两点”模型。

思考:解释一下在浏览器里点击页面链接后发生了哪些事情吗?

https://mp.weixin.qq.com/s/nMlZWZO6foRUPFK34ouPhg

http(s)协议 - 图5

1、如果域名不是ip,需要走域名解析成ip的逻辑,优先级顺序为: 1 浏览器缓存 > 2 系统缓存 > 3 本地hosts >4 根域名 > 5 顶级dns服务器(如 com) > 6 二级dns服务器(baidu.com) > 7 三级dns服务器(www.baidu.com),如果客户端指向的dns服务器为非官方的如 8.8.8.8,那在第4步之前可能还有一层cache,当然最后解析的ip有可能是cdn的,如果cdn失效了就直接穿透到源ip,当然这个服务器这一部分可能做了四层负载均衡的设置,所以有可能每次获取的服务器ip都不一祥,也有可能到了服务器ngx层做了七层转发,所以虽然获得的ip一样,但是内部可能转发给了很多内网服务器

2、通过中间各种路由器的转发,找到了最终服务器,进行tcp三次握手,数据请求,请求分两种一种是uri请求,一种是浏览器咸吃萝卜淡操心的请求网站图标ico的资源请求,然后服务端收到请求后进行请求分析,最终返回http报文,再通过tcp这个连接隧道返回给用户端,用户端收到后再告诉服务端已经收到结果的信号(ack),然后客户端有一套解析规则,如果是html,可能还有额外的外部连接请求,是跟刚才的请求流程是同理的(假设是http1.1),只不过没有了tcp三次握手的过程,最终用户看到了百度的搜索页面。当然如果dns没解析成功,浏览器直接就报错了,不会继续请求接下来的资源

八、HTTP报文是什么样子

九、深入理解请求方法

目前 HTTP/1.1 规定了八种方法,单词都必须是大写的形式

  • GET:获取资源,可以理解为读取或者下载数据;
  • HEAD:获取资源的元信息;
  • POST:向资源提交数据,相当于写入或上传数据;
  • PUT:类似 POST;DELETE:删除资源;
  • CONNECT:建立特殊的连接隧道;
  • OPTIONS:列出可对资源实行的方法;
  • TRACE:追踪请求 - 响应的传输路径。

http(s)协议 - 图6

1.GET/HEAD

GET 方法应该是 HTTP 协议里最知名的请求方法了,也应该是用的最多的,自 0.9 版出现并一直被保留至今,是名副其实的“元老”。

它的含义是请求从服务器获取资源,这个资源既可以是静态的文本、页面、图片、视频,也可以是由 PHP、Java 动态生成的页面或者其他格式的数据。

GET 方法虽然基本动作比较简单,但搭配 URI 和其他头字段就能实现对资源更精细的操作。

例如,在 URI 后使用“#”,就可以在获取页面后直接定位到某个标签所在的位置;使用 If-Modified-Since 字段就变成了“有条件的请求”,仅当资源被修改时才会执行获取动作;使用 Range 字段就是“范围请求”,只获取资源的一部分数据。

HEAD 方法与 GET 方法类似,也是请求从服务器获取资源,服务器的处理机制也是一样的,但服务器不会返回请求的实体数据,只会传回响应头,也就是资源的“元信息”。

HEAD 方法可以看做是 GET 方法的一个“简化版”或者“轻量版”。因为它的响应头与 GET 完全相同,所以可以用在很多并不真正需要资源的场合,避免传输 body 数据的浪费。

比如,想要检查一个文件是否存在,只要发个 HEAD 请求就可以了,没有必要用 GET 把整个文件都取下来。再比如,要检查文件是否有最新版本,同样也应该用 HEAD,服务器会在响应头里把文件的修改时间传回来。

2.POST/PUT

POST 和 PUT 方法,这两个方法也很像。

GET 和 HEAD 方法是从服务器获取数据,而 POST 和 PUT 方法则是相反操作,向 URI 指定的资源提交数据,数据就放在报文的 body 里。

POST 也是一个经常用到的请求方法,使用频率应该是仅次于 GET,应用的场景也非常多,只要向服务器发送数据,用的大多数都是 POST。

比如,你上论坛灌水,敲了一堆字后点击“发帖”按钮,浏览器就执行了一次 POST 请求,把你的文字放进报文的 body 里,然后拼好 POST 请求头,通过 TCP 协议发给服务器。

又比如,你上购物网站,看到了一件心仪的商品,点击“加入购物车”,这时也会有 POST 请求,浏览器会把商品 ID 发给服务器,服务器再把 ID 写入你的购物车相关的数据库记录。

PUT 的作用与 POST 类似,也可以向服务器提交数据,但与 POST 存在微妙的不同,通常 POST 表示的是“新建”“create”的含义,而 PUT 则是“修改”“update”的含义。

在实际应用中,PUT 用到的比较少。而且,因为它与 POST 的语义、功能太过近似,有的服务器甚至就直接禁止使用 PUT 方法,只用 POST 方法上传数据。

3.其他方法

讲完了 GET/HEAD/POST/PUT,还剩下四个标准请求方法,它们属于比较“冷僻”的方法,应用的不是很多。

DELETE 方法指示服务器删除资源,因为这个动作危险性太大,所以通常服务器不会执行真正的删除操作,而是对资源做一个删除标记。当然,更多的时候服务器就直接不处理 DELETE 请求。

CONNECT 是一个比较特殊的方法,要求服务器为客户端和另一台远程服务器建立一条特殊的连接隧道,这时 Web 服务器在中间充当了代理的角色。

OPTIONS 方法要求服务器列出可对资源实行的操作方法,在响应头的 Allow 字段里返回。它的功能很有限,用处也不大,有的服务器(例如 Nginx)干脆就没有实现对它的支持。

TRACE 方法多用于对 HTTP 链路的测试或诊断,可以显示出请求 - 响应的传输路径。它的本意是好的,但存在漏洞,会泄漏网站的信息,所以 Web 服务器通常也是禁止使用。

4.安全与幂等

关于请求方法还有两个面试时有可能会问到、比较重要的概念:安全与幂等。

在 HTTP 协议里,所谓的“安全”是指请求方法不会“破坏”服务器上的资源,即不会对服务器上的资源造成实质的修改。

按照这个定义,只有 GET 和 HEAD 方法是“安全”的,因为它们是“只读”操作,只要服务器不故意曲解请求方法的处理方式,无论 GET 和 HEAD 操作多少次,服务器上的数据都是“安全的”。

而 POST/PUT/DELETE 操作会修改服务器上的资源,增加或删除数据,所以是“不安全”的。

所谓的“幂等”实际上是一个数学用语,被借用到了 HTTP 协议里,意思是多次执行相同的操作,结果也都是相同的,即多次“幂”后结果“相等”。

很显然,GET 和 HEAD 既是安全的也是幂等的,DELETE 可以多次删除同一个资源,效果都是“资源不存在”,所以也是幂等的。

POST 和 PUT 的幂等性质就略费解一点。

按照 RFC 里的语义,POST 是“新增或提交数据”,多次提交数据会创建多个资源,所以不是幂等的;而 PUT 是“替换或更新数据”,多次更新一个资源,资源还是会第一次更新的状态,所以是幂等的。

可以对比一下 SQL 来加深理解:把 POST 理解成 INSERT,把 PUT 理解成 UPDATE,这样就很清楚了。多次 INSERT 会添加多条记录,而多次 UPDATE 只操作一条记录,而且效果相同。

5.总结:

  • 请求方法是客户端发出的、要求服务器执行的、对资源的一种操作;
  • 请求方法是对服务器的“指示”,真正应如何处理由服务器来决定;
  • 最常用的请求方法是 GET 和 POST,分别是获取数据和发送数据;
  • HEAD 方法是轻量级的 GET,用来获取资源的元信息;
  • PUT 基本上是 POST 的同义词,多用于更新数据;
  • “安全”与“幂等”是描述请求方法的两个重要属性,具有理论指导意义,可以帮助我们设计系统。

6.补充:关于options请求

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
在现在前端最常用的 cors 跨域中,浏览器都是用 OPTIONS 方法发预检请求的

十、响应状态码

  • 1××:提示信息,表示目前是协议处理的中间状态,还需要后续的操作;
  • 2××:成功,报文已经收到并被正确处理;
  • 3××:重定向,资源位置发生变动,需要客户端重新发送请求;
  • 4××:客户端错误,请求报文有误,服务器无法处理;
  • 5××:服务器错误,服务器在处理请求时内部发生了错误。

1××

1××类状态码属于提示信息,是协议处理的中间状态,实际能够用到的时候很少。

我们偶尔能够见到的是“101 Switching Protocols”。它的意思是客户端使用 Upgrade 头字段,要求在 HTTP 协议的基础上改成其他的协议继续通信,比如 WebSocket。而如果服务器也同意变更协议,就会发送状态码 101,但这之后的数据传输就不会再使用 HTTP 了。

2××

2××类状态码表示服务器收到并成功处理了客户端的请求,这也是客户端最愿意看到的状态码。

“200 OK”是最常见的成功状态码,表示一切正常,服务器如客户端所期望的那样返回了处理结果,如果是非 HEAD 请求,通常在响应头后都会有 body 数据。

“204 No Content”是另一个很常见的成功状态码,它的含义与“200 OK”基本相同,但响应头后没有 body 数据。所以对于 Web 服务器来说,正确地区分 200 和 204 是很必要的。

“206 Partial Content”是 HTTP 分块下载或断点续传的基础,在客户端发送“范围请求”、要求获取资源的部分数据时出现,它与 200 一样,也是服务器成功处理了请求,但 body 里的数据不是资源的全部,而是其中的一部分。

状态码 206 通常还会伴随着头字段“Content-Range”,表示响应报文里 body 数据的具体范围,供客户端确认,例如“Content-Range: bytes 0-99/2000”,意思是此次获取的是总计 2000 个字节的前 100 个字节。

3××

3××类状态码表示客户端请求的资源发生了变动,客户端必须用新的 URI 重新发送请求获取资源,也就是通常所说的“重定向”,包括著名的 301、302 跳转。

301 Moved Permanently”俗称“永久重定向”,含义是此次请求的资源已经不存在了,需要改用改用新的 URI 再次访问。

“302 Found”,曾经的描述短语是“Moved Temporarily”,俗称“临时重定向”,意思是请求的资源还在,但需要暂时用另一个 URI 来访问。

301 和 302 都会在响应头里使用字段 Location 指明后续要跳转的 URI,最终的效果很相似,浏览器都会重定向到新的 URI。两者的根本区别在于语义,一个是“永久”,一个是“临时”,所以在场景、用法上差距很大。

比如,你的网站升级到了 HTTPS,原来的 HTTP 不打算用了,这就是“永久”的,所以要配置 301 跳转,把所有的 HTTP 流量都切换到 HTTPS。

再比如,今天夜里网站后台要系统维护,服务暂时不可用,这就属于“临时”的,可以配置成 302 跳转,把流量临时切换到一个静态通知页面,浏览器看到这个 302 就知道这只是暂时的情况,不会做缓存优化,第二天还会访问原来的地址。

“304 Not Modified” 是一个比较有意思的状态码,它用于 If-Modified-Since 等条件请求,表示资源未修改,用于缓存控制。它不具有通常的跳转含义,但可以理解成“重定向已到缓存的文件”(即“缓存重定向”)。301、302 和 304 分别涉及了 HTTP 协议里重要的“重定向跳转”和“缓存控制”,在之后的课程中我还会细讲。

4××

4××类状态码表示客户端发送的请求报文有误,服务器无法处理,它就是真正的“错误码”含义了。

“400 Bad Request”是一个通用的错误码,表示请求报文有错误,但具体是数据格式错误、缺少请求头还是 URI 超长它没有明确说,只是一个笼统的错误,客户端看到 400 只会是“一头雾水”“不知所措”。所以,在开发 Web 应用时应当尽量避免给客户端返回 400,而是要用其他更有明确含义的状态码。

“403 Forbidden”实际上不是客户端的请求出错,而是表示服务器禁止访问资源。原因可能多种多样,例如信息敏感、法律禁止等,如果服务器友好一点,可以在 body 里详细说明拒绝请求的原因,不过现实中通常都是直接给一个“闭门羹”。

“404 Not Found”可能是我们最常看见也是最不愿意看到的一个状态码,它的原意是资源在本服务器上未找到,所以无法提供给客户端。但现在已经被“用滥了”,只要服务器“不高兴”就可以给出个 404,而我们也无从得知后面到底是真的未找到,还是有什么别的原因,某种程度上它比 403 还要令人讨厌。

4××里剩下的一些代码较明确地说明了错误的原因,都很好理解,开发中常用的有:

  • 405 Method Not Allowed:不允许使用某些方法操作资源,例如不允许 POST 只能 GET;
  • 406 Not Acceptable:资源无法满足客户端请求的条件,例如请求中文但只有英文;
  • 408 Request Timeout:请求超时,服务器等待了过长的时间;
  • 409 Conflict:多个请求发生了冲突,可以理解为多线程并发时的竞态;
  • 413 Request Entity Too Large:请求报文里的 body 太大;
  • 414 Request-URI Too Long:请求行里的 URI 太大;
  • 429 Too Many Requests:客户端发送了太多的请求,通常是由于服务器的限连策略;
  • 431 Request Header Fields Too Large:请求头某个字段或总体太大;

5××

5××类状态码表示客户端请求报文正确,但服务器在处理时内部发生了错误,无法返回应有的响应数据,是服务器端的“错误码”。

“500 Internal Server Error”与 400 类似,也是一个通用的错误码,服务器究竟发生了什么错误我们是不知道的。不过对于服务器来说这应该算是好事,通常不应该把服务器内部的详细信息,例如出错的函数调用栈告诉外界。虽然不利于调试,但能够防止黑客的窥探或者分析。

“501 Not Implemented”表示客户端请求的功能还不支持,这个错误码比 500 要“温和”一些,和“即将开业,敬请期待”的意思差不多,不过具体什么时候“开业”就不好说了。

“502 Bad Gateway”通常是服务器作为网关或者代理时返回的错误码,表示服务器自身工作正常,访问后端服务器时发生了错误,但具体的错误原因也是不知道的。

“503 Service Unavailable”表示服务器当前很忙,暂时无法响应服务,我们上网时有时候遇到的“网络服务正忙,请稍后重试”的提示信息就是状态码 503。

503 是一个“临时”的状态,很可能过几秒钟后服务器就不那么忙了,可以继续提供服务,所以 503 响应报文里通常还会有一个“Retry-After”字段,指示客户端可以在多久以后再次尝试发送请求。

十一、HTTP特点

http(s)协议 - 图7

灵活可扩展

可靠传输

HTTP 并不能 100% 保证数据一定能够发送到另一端,在网络繁忙、连接质量差等恶劣的环境下,也有可能收发失败。“可靠”只是向使用者提供了一个“承诺”,会在下层用多种手段“尽量”保证数据的完整送达。

应用层协议

虽然应用层有许多的协议,但它们都仅关注很小的应用领域,局限在很少的应用场景。例如 FTP 只能传输文件、SMTP 只能发送邮件、SSH 只能远程登录等,在通用的数据传输方面“完全不能打”。

HTTP 凭借着可携带任意头字段和实体数据的报文结构,以及连接控制、缓存代理等方便易用的特性,一出现就“技压群雄”,迅速成为了应用层里的“明星”协议。

请求 - 应答

通俗来讲就是“一发一收”“有来有去”,请求方先发起连接和请求,是主动的,而应答方只有在收到请求后才能答复,是被动的,如果没有请求时不会有任何动作。

无状态

“没有记忆能力”,缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。

每个请求都是互相独立、毫无关联的,协议不要求客户端或服务器记录请求相关的信息。

于是,两种用于保持HTTP连接状态的技术就应运而生了,一个是Cookie,而另一个则是Session

十二、HTTP有哪些优点哪些缺点

十三、HTTP传输大文件的方法

1.数据压缩

浏览器发送请求时的“Accept-Encoding”头字段,携带了浏览器支持的压缩方式列表 gzip、deflate、br,这样服务器就可以从中选择一种压缩算法,放进“Content-Encoding”响应头里,再把原数据压缩后发给浏览器。

不过这个解决方法也有个缺点,gzip 等压缩算法通常只对文本文件有较好的压缩率,而图片、音频视频等多媒体数据本身就已经是高度压缩的,再用 gzip 处理也不会变小(甚至还有可能会增大一点),所以它就失效了。

不过数据压缩在处理文本的时候效果还是很好的,所以各大网站的服务器都会使用这个手段作为“保底”。例如,在 Nginx 里就会使用“gzip on”指令,启用对“text/html”的压缩。

2.分块传输

如果大文件整体不能变小,那就把它“拆开”,分解成多个小块,把这些小块分批发给浏览器,浏览器收到后再组装复原。

这样浏览器和服务器都不用在内存里保存文件的全部,每次只收发一小部分,网络也不会被大文件长时间占用,内存、带宽等资源也就节省下来了。

在响应报文里用头字段“Transfer-Encoding: chunked”来表示,意思是报文里的 body 部分不是一次性发过来的,而是分成了许多的块(chunk)逐个发送。

分块传输也可以用于“流式数据”,例如由数据库动态生成的表单页面,这种情况下 body 数据的长度是未知的,无法在头字段“Content-Length”里给出确切的长度,所以也只能用 chunked 方式分块发送。

“Transfer-Encoding: chunked”“Content-Length”这两个字段是互斥的,也就是说响应报文里这两个字段不能同时出现,一个响应报文的传输要么是长度已知,要么是长度未知(chunked)。

3.范围请求

设想一下,你在看当下正热播的某穿越剧,想跳过片头,直接看正片,或者有段剧情很无聊,想拖动进度条快进几分钟,这实际上是想获取一个大文件其中的片段数据,而分块传输并没有这个能力。

“范围请求”,允许客户端在请求头里使用专用字段来表示只获取文件的一部分。

要想实现范围请求就需要服务器必须在响应头里使用字段“Accept-Ranges: bytes”明确告知客户端:“我是支持范围请求的”。

服务器收到 Range 字段后,需要做四件事。

  1. 第一,它必须检查范围是否合法,比如文件只有 100 个字节,但请求“200-300”,这就是范围越界了。服务器就会返回状态码 416,意思是“你的范围请求有误,我无法处理,请再检查一下”。
  2. 第二,如果范围正确,服务器就可以根据 Range 头计算偏移量,读取文件的片段了,返回状态码“206 Partial Content”,和 200 的意思差不多,但表示 body 只是原数据的一部分。
  3. 第三,服务器要添加一个响应头字段 Content-Range,告诉片段的实际偏移量和资源的总大小,格式是“bytes x-y/length”,与 Range 头区别在没有“=”,范围后多了总长度。例如,对于“0-10”的范围请求,值就是“bytes 0-10/100”。
  4. 最后剩下的就是发送数据了,直接把片段用 TCP 发给客户端,一个范围请求就算是处理完了。

不仅看视频的拖拽进度需要范围请求,常用的下载工具里的多段下载、断点续传也是基于它实现的,要点是:

  • 先发个 HEAD,看服务器是否支持范围请求,同时获取文件的大小;
  • 开 N 个线程,每个线程使用 Range 字段划分出各自负责下载的片段,发请求传输数据;
  • 下载意外中断也不怕,不必重头再来一遍,只要根据上次的下载记录,用 Range 请求剩下的那一部分就可以了。

4.多段数据

刚才说的范围请求一次只获取一个片段,其实它还支持在 Range 头里使用多个“x-y”,一次性获取多个片段数据。

这时候响应报文的数据类型是“multipart/byteranges”,body 里的多个部分会用 boundary 字符串分隔。

多段数据的格式与分块传输也比较类似,但它需要用分隔标记 boundary 来区分不同的片段。

十四、HTTP的连接管理

1.短连接

早期的(0.9/1.0) HTTP 协议使用短连接,每次发送请求前需要先与服务器建立连接,收到响应报文后会立即关闭连接。但是建立连接和关闭连接都是非常昂贵的操作,需要经过三次握手四次挥手,每次会浪费很多时间,传输效率很低;

http(s)协议 - 图8

2.长连接

也叫“持久连接”,采用的是“成本均摊”的思路,在一个连接上收发多个请求响应,而不是立即关闭连接,提高了传输效率;

http(s)协议 - 图9

长连接的缺点:TCP连接长时间不关闭,会占用服务器的资源。如果有大量的空闲长连接只连不发,就会很快耗尽服务器的资源,导致服务器无法为真正有需要的用户提供服务。要想关闭连接需要在请求头加上“Connection: close”字段,告诉服务器:“这次通信后就关闭连接”

3.连接相关的头字段

HTTP/1.1 中的连接都会默认启用长连接。不需要用什么特殊的头字段指定,只要向服务器发送了第一次请求,后续的请求都会重复利用第一次打开的 TCP 连接,也就是长连接,在这个连接上收发数据。

服务器端通常不会主动关闭连接,但也可以使用一些策略。

我们也可以在请求头里明确地要求使用长连接机制,使用的字段是 Connection,值是“keep-alive”

客户端和服务器都可以在报文里附加通用头字段“Keep-Alive: timeout=value”,限定长连接的超时时间。

4.队头阻塞

“队头阻塞”与短连接和长连接无关,而是由 HTTP 基本的“请求 - 应答”模型所导致的。因为 HTTP 规定报文必须是“一发一收”,这就形成了一个先进先出的“串行”队列。排在最前面的请求被最优先处理。队首的请求因为处理的太慢耽误了时间,那么队列里后面的所有请求也不得不跟着一起等待,结果就是其他的请求承担了不应有的时间成本。

http(s)协议 - 图10

性能优化

1.并发连接

同时对一个域名发起多个长连接,用数量来解决质量的问题。为了防止连接过多,给服务器造成压力,规定6-8个连接

2.域名分片

对同一服务器上的资源多开几个域名。

总结:

短连接:每次“请求-响应”都先建立tcp连接,完后关闭连接。这样三次握手1.5个rtt,“请求-响应”2个rtt(里面有两个ack),四次挥手2个rtt,效率极低。适用于少次请求,例如客户端只会对某个服务器发送几次请求后就不再发送。

长连接:建立tcp连接后不立即关闭,后续http请求复用这个tcp连接。http/1.1默认开启。如果有大量的空闲长连接只连不发占用资源,可能导致耗尽资源“拒绝服务”即DDoS。因此服务器常会设置超时时间或最大请求数。

十五、HTTP的重定向和跳转

点击页面“链接”时的跳转是主动跳转,还有一种被动跳转,叫做“重定向”(Redirection)

1.重定向的过程

浏览器收到 301/302 报文,会检查响应头里有没有“Location”。如果有,就从字段值里提取出 URI,发出新的 HTTP 请求。

在重定向时如果只是在站内跳转,你可以放心地使用相对 URI。但如果要跳转到站外,就必须用绝对 URI。

2.重定向状态码

301 俗称“永久重定向”(Moved Permanently),意思是原 URI 已经“永久”性地不存在了,今后的所有请求都必须改用新的 URI。

浏览器看到 301,就知道原来的 URI“过时”了,就会做适当的优化。比如历史记录、更新书签,下次可能就会直接用新的 URI 访问,省去了再次跳转的成本。搜索引擎的爬虫看到 301,也会更新索引库,不再使用老的 URI。

302 俗称“临时重定向”(“Moved Temporarily”),意思是原 URI 处于“临时维护”状态,新的 URI 是起“顶包”作用的“临时工”。

浏览器或者爬虫看到 302,会认为原来的 URI 仍然有效,但暂时不可用,所以只会执行简单的跳转页面,不记录新的 URI,也不会有其他的多余动作,下次访问还是用原 URI。

  • 303 See Other:类似 302,但要求重定向后的请求改为 GET 方法,访问一个结果页面,避免 POST/PUT 重复操作;
  • 307 Temporary Redirect:类似 302,但重定向后请求里的方法和实体不允许变动,含义比 302 更明确;
  • 308 Permanent Redirect:类似 307,不允许重定向后的请求变动,但它是 301“永久重定向”的含义。

3.重定向的应用场景

“资源不可用”,需要用另一个新的 URI 来代替。例如域名变更、服务器变更、网站改版、系统维护

“避免重复”,让多个网址都跳转到一个 URI,增加访问入口的同时还不会增加额外的工作量。

如果域名、服务器、网站架构发生了大幅度的改变,比如启用了新域名、服务器切换到了新机房、网站目录层次重构,这些都算是“永久性”的改变。原来的 URI 已经不能用了,必须用 301“永久重定向”

原来的 URI 在将来的某个时间点还会恢复正常,常见的应用场景就是系统维护,把网站重定向到一个通知页面,告诉用户过一会儿再来访问。另一种用法就是“服务降级”,比如在双十一促销的时候,把订单查询、领积分等不重要的功能入口暂时关闭,保证核心服务能够正常运行。

十六、HTTP的cookie机制

HTTP是无状态的,没有记忆能力,记不住谁是谁,每次请求都是独立的,无法完成一整套的业务逻辑。Cookie通过在客户端记录信息确定用户身份

1.Cookie 的工作过程

1.1服务器生成cookie并发送给客户端

当用户通过浏览器第一次访问服务器的时候,服务器肯定是不知道他的身份的。所以,就要创建一个独特的身份标识数据,格式是“key=value”,然后放进 Set-Cookie 字段里,随着响应报文一同发给浏览器。

1.2客户端保存并发送cookie到服务器

浏览器收到响应报文,看到里面有 Set-Cookie,知道这是服务器给的身份标识,于是就保存起来,下次再请求的时候就自动把这个值放进 Cookie 字段里发给服务器。

http(s)协议 - 图11

2.Cookie的属性

2.1 设置Cookie 的生存周期

“Expires”俗称“过期时间”,用的是绝对时间点,可以理解为“截止日期”。

“Max-Age”用的是相对时间,单位是秒,浏览器用收到报文的时间点再加上 Max-Age,就可以得到失效的绝对时间。

2.2 设置 Cookie 的作用域

“Domain”“Path”指定了 Cookie 所属的域名和路径,浏览器在发送 Cookie 前会从 URI 中提取出 host 和 path 部分,对比 Cookie 的属性。如果不满足条件,就不会在请求头里发送 Cookie。

2.3 Cookie 的安全性

“HttpOnly”

在 JS 脚本里可以用 document.cookie 来读写 Cookie 数据,这就带来了安全隐患,有可能会导致“跨站脚本”(XSS)https://www.cnblogs.com/eco-just/p/9484905.html攻击窃取数据。

属性“HttpOnly”会告诉浏览器,此 Cookie 只能通过浏览器 HTTP 协议传输,禁止其他方式访问,浏览器的 JS 引擎就会禁用 document.cookie 等一切相关的 API,脚本攻击也就无从谈起了。

“SameSite”

属性“SameSite”可以防范“跨站请求伪造”(XSRF)https://www.cnblogs.com/Erik_Xu/p/5481441.html https://www.cnblogs.com/springlight/p/6229363.html攻击,设置成“SameSite=Strict”可以严格限定 Cookie 不能随着跳转链接跨站发送,而“SameSite=Lax”则略宽松一点,允许 GET/HEAD 等安全方法,但禁止 POST 跨站发送。

“Secure”

表示这个 Cookie 仅能用 HTTPS 协议加密传输,明文的 HTTP 协议会禁止发送。但 Cookie 本身不是加密的,浏览器里还是以明文的形式存在。

3.Cookie 主要用于以下三个方面:

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)

4.Cookie的缺陷

  • cookie会被附加在每个HTTP请求中,所以无形中增加了流量。
  • 由于在HTTP请求中的cookie是明文传递的,所以安全性成问题。(除非用HTTPS)
  • Cookie的大小限制在4KB左右。对于复杂的存储需求来说是不够用的。

十七、HTTP的缓存策略

由于链路漫长,网络时延不可控,浏览器使用 HTTP 获取资源的成本较高。所以,非常有必要把“来之不易”的数据缓存起来,下次再请求的时候尽可能地复用。这样,就可以避免多次请求 - 应答的通信成本,节约网络带宽,也可以加快响应速度。

https://mp.weixin.qq.com/s/G5FIrWOtsNROHgEKesJcdg

1.强制缓存

强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程,强制缓存的情况主要有三种(暂不分析协商缓存过程),如下:不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求(跟第一次发起请求一致),如下图:

控制强制缓存的字段分别是ExpiresCache-Control,其中Cache-Control优先级比Expires高。

1.1 Cache-Control

在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存,主要取值为:

  • public:所有内容都将被缓存(客户端和代理服务器都可缓存)
  • private:所有内容只有客户端可以缓存,Cache-Control的默认取值
  • max-age:表示资源的有效期
  • no-store:不允许缓存,用于某些变化非常频繁的数据,例如秒杀页面;
  • no-cache:它的字面含义容易与 no-store 搞混,实际的意思并不是不允许缓存,而是可以缓存,但在使用之前必须要去服务器验证是否过期,是否有最新的版本;
  • must-revalidate:又是一个和 no-cache 相似的词,它的意思是如果缓存不过期就可以继续使用,但过期了如果还想用就必须去服务器验证。

http(s)协议 - 图12

以我的博客内容为例进行第一次请求:

  • expires的时间值,是一个绝对值
  • Cache-Control为max-age=900,是相对值。在无法确定客户端的时间是否与服务端的时间同步的情况下,Cache-Control相比于expires是更好的选择,所以同时存在时,只有Cache-Control生效。
  • “private”表示缓存只能在客户端保存,是用户“私有”的,不能放在代理上与别人共享。如果是“public”,意思就是缓存完全开放,谁都可以存,谁都可以用。

意思就是说在600秒内再次发起该请求,则会直接使用缓存结果,强制缓存生效。刷新看看:

memory cache代表使用内存中的缓存, disk cache则代表使用的是硬盘中的缓存,浏览器读取缓存的顺序为memory –> disk

  • 内存缓存(from memory cache):内存缓存具有两个特点,分别是快速读取和时效性:
  • 快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取。
  • 时效性:一旦该进程关闭,则该进程的内存则会清空。
  • 硬盘缓存(from disk cache):硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。

2.协商缓存

协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:协商缓存生效,返回304,如下:

协商缓存失效,返回200和请求结果结果,如下

控制协商缓存的字段分别有:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。

2.1 Last-Modified / If-Modified-Since

Last-Modified服务器响应请求时,返回该资源文件在服务器最后被修改的时间

If-Modified-Since则是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。

服务器会根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件,

2.2 Etag / If-None-Match

ETag 是“实体标签”(Entity Tag)的缩写,是资源的一个唯一标识,主要是用来解决修改时间无法准确区分文件变化的问题。

ETag 还有“强”“弱”之分。

强 ETag 要求资源在字节级别必须完全相符,弱 ETag 在值前有个“W/”标记,只要求资源在语义上没有变化,但内部可能会有部分发生了改变(例如 HTML 里的标签顺序调整,或者多了几个空格)。

If-None-Match是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有If-None-Match,则会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200。

总结:

强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,重新获取请求结果,再存入浏览器缓存中;生效则返回304,继续使用缓存。

十八、HTTP代理服务

1.代理服务

HTTP代理就是介于浏览器和web服务器之间的一台服务器,连接代理后,浏览器不再直接向web服务器取回网页,而是向代理服务器发出request信号,代理服务器再想web服务器发出请求,收到web服务器返回的数据后再反馈给浏览器。

代理服务具有双重身份:面向下游的用户时,表现为服务器,代表源服务器响应客户端的请求;而面向上游的源服务器时,又表现为客户端,代表客户端发送请求。

2.代理的作用

2.1 负载均衡

因为在面向客户端时屏蔽了源服务器,客户端看到的只是代理服务器,源服务器究竟有多少台、是哪些 IP 地址都不知道。于是代理服务器就可以掌握请求分发的“大权”,决定由后面的哪台服务器来响应请求。

http(s)协议 - 图13

在负载均衡的同时,代理服务还可以执行更多的功能,比如:

  • 健康检查:使用“心跳”等机制监控后端服务器,发现有故障就及时“踢出”集群,保证服务高可用;
  • 安全防护:保护被代理的后端服务器,限制 IP 地址或流量,抵御网络攻击和过载;
  • 加密卸载:对外网使用 SSL/TLS 加密通信认证,而在安全的内网不加密,消除加解密成本;
  • 数据过滤:拦截上下行的数据,任意指定策略修改请求或者响应;
  • 内容缓存:暂存、复用服务器响应

3.代理相关头字段

Via 是一个通用字段,请求头或响应头里都可以出现。每当报文经过一个代理节点,代理服务器就会把自身的信息追加到字段的末尾,就像是经手人盖了一个章。多个代理会形成一个列表。

Via 字段只解决了客户端和源服务器判断是否存在代理的问题,还不能知道对方的真实信息。

如果想要知道客户端的真实 IP 地址,可以使用字段“X-Forwarded-For”和“X-Real-IP”;但是对于匿名代理,不一定可以通过这两个字段获取到真实的ip

十九、HTTP的缓存代理

HTTP 的服务器缓存功能主要由代理服务器来实现(即缓存代理)。

1.缓存代理服务

代理服务收到源服务器发来的响应数据后需要做两件事。第一个当然是把报文转发给客户端,而第二个就是把报文存入自己的 Cache 里。

下一次再有相同的请求,代理服务器就可以直接发送 304 或者缓存数据,不必再从源服务器那里获取。这样就降低了客户端的等待时间,同时节约了源服务器的网络带宽。

2.源服务器的缓存控制

2.1 区分客户端上的缓存和代理上的缓存

使用两个新属性“private”“public”

“private”表示缓存只能在客户端保存,是用户“私有”的,不能放在代理上与别人共享。而“public”的意思就是缓存完全开放,谁都可以存,谁都可以用。比如Cookie就属于私人数据,不能存在代理上,不然,别人访问代理获取了被缓存的响应就麻烦了。

2.2 缓存时效后的验证

“must-revalidate”是只要过期就必须回源服务器验证

“proxy-revalidate”只要求代理的缓存过期后必须验证,客户端不必回源

2.3 缓存生效时间

”s-maxage”(s 是 share 的意思,注意 maxage 中间没有“-”),只限定在代理上能够存多久,而客户端仍然使用“max-age”。

“no-transform”,不允许代理对缓存下来的数据进行优化操作。

http(s)协议 - 图14

3.客户端的缓存控制

max-age、no-store、no-cache 这三个属性在前面介绍过了,它们也是同样作用于代理和源服务器。

关于缓存的生存时间,多了两个新属性“max-stale”和“min-fresh”

“max-stale”的意思是如果代理上的缓存过期了也可以接受,但不能过期太多,超过 x 秒也会不要。“min-fresh”的意思是缓存必须有效,而且必须在 x 秒后依然有效。

思考:有缓存代理时浏览器的工作流程

  • ip+port: 那直接请求对应的服务器 。
  • 域名: 那开展一系列的dns递归查询:先拿dns缓存,没有缓存->本地dns服务器->根dns服务器->顶级dns服务器->权威dns服务器->GSLB,查到ip返回最优ip组实现负载均衡,浏览器随机或者轮询取一个ip开始它的http请求之旅。
  • 浏览器判断该网页是否允许缓存,然后添加Cache-Control的各种字段no-store是否允许缓存/no-cache缓存必须进行验证/noly-if-cached只接受代理的缓存等,max-age最大生存时间 max-stale 短时间过期可用 min-fresh 最短有效时间等。If-Modified-Since/if-None-Match/Last-modified/ETag等字段用于判断服务端是否有更新。然后将请求发给代理服务器。请求代理服务器,如果是第一次,要经历浏览器和代理服务器的3次tcp握手进行连接,连接成功,发送http请求。
  • 代理服务器响应客户端的请求,首先查看是否允许缓存,允许那就查看自己本地缓存有没有,通过查看max-age/max-stale/min-fresh等信息判断是否过期,没有过期直接拿来用,将数据返回给客户端。如果过期了,代理服务器将用客户端的请求,再次像真实服务器进行请求。如果也是第一次连接,需要经历代理服务器和真实服务器的3次tcp握手,连接成功,发送请求。
  • 真实服务器收到请求之后,通过if-Modified-Since/Last-Modified/if-None-Match/ETag等字段判断是否有更新,没有更新,直接返回304。如果有更新,则将数据打包http response 返回。返回头字段会添加Cache-Control字段,用来判断缓存的控制策略以及生存周期,no-store不允许缓存/no-cache使用缓存必须先验证/must-revalidate缓存不过期可用过期必须重新请求验证/proxy-revalidate缓存过期只要求代理进行请求验证 private不能在代理层保存只能在客户端保存/public缓存完全开放 s-maxage缓存在代理上可以缓存的时间 no-transform不允许代理对缓存做任何的改动。然后根据业务需求判断该地址是不是需要重定向,如果需要是短期的重定向还是永久的重定向,按需将状态码修改为301或者302。
  • 最后真实服务器将数据打包成http相应 回给代理服务器。
  • 代理服务器收到真实服务器的回应数据,首先会查看Cache-Control里的字段,是否允许它进行缓存,如果是private,代理服务器不进行缓存,直接返回给客户端。public则根据s-maxage/no-transform进行缓存,如果可以优化并且代理服务器需要优化,那可能会先优化数据,否则同时将数据回发给客户端。
  • 客户端收到数据,如果是304,则直接拿缓存数据进行渲染,并修改相关缓存变量,比如时间,以及缓存使用策略。如果收到了301或者302,那么客户端会再次发起新的url请求,进行跳转到最终的页面。
  • 最后,底层tcp 经过4次挥手,完成关闭连接。

二十、HTTPS

HTTP 为什么不安全

探究 HTTPS 安全之前,我们应该先探究 HTTP 为什么不安全。

首先得了解计算机网络相关的知识,面试经常会被问到的 TCP/IP 三次握手之类的知识。从《计算机网络》中我们得知网络中的数据是以包的形式在网络模型各层之间传递的,HTTP 协议位于应用层,TCP/IP 协议的上层,而数据报文在这些层之间都是没有加密的明文,这就导致了一下的问题:

通信是采用明文(不加密),内容可能被窃听

http(s)协议 - 图15

从图中我们可以看出,我们作为客户端处于庞大互联网中的一个节点,而在通信时,中间会经历很多个阶段,而每个阶段报文内容都有可能会被窃听。

无法验证接受报文的完整性,可能已被篡改

无法验证完整性,即意味着接收到的报文可能是缺失的,也意味着接收到的报文可能是错误的,也就是不是我们想要的。

http(s)协议 - 图16

作为客户端向服务端请求数据,服务器响应的是 A 内容,客户端接收到的是 B 内容,但是客户端并不知道 A 和 B 是否相同,因为传输过程中报文可能被篡改了,这个过程工厂被称为“中间人攻击”。

http(s)协议 - 图17

HTTP 协议不验证通信方身份,因此可能被伪装

http(s)协议 - 图18

HTTP 协议设计的十分简单,并且不验证通信双方,也就意味着,不论是谁发送的请求,只要合法(后台没有限制访问 ip 和端口号),服务器都会接受,而不确认通信方身份可能会导致以下的问题:

  • 无法确定请求发送至目标 Web 服务器是否按照真实意图返回响应的那台服务器,有可能是已经伪装的 Web 服务器
  • 无法确定响应返回到的客户端是否是按照真实意图接收响应的客户端,有可能是已伪装的客户端
  • 无法确定正在通信的对方是否具有访问权限,因为某些 Web 服务器上保存着重要信息,只想发给特定用户通信的权限
  • 无法判断请求是来自何方,出自谁手
  • 即是无意义的请求也照单全收,无法阻止海量请求下的 DoS 攻击(Denial of Service),也就容易遭受攻击

二十一、HTTPS 实现原理

由于 HTTP 这么不安全,所以为了解决上述的问题,HTTPS 应运而生。目前大部分的网站都已经过渡到了 HTTPS。

http(s)协议 - 图19

http(s)协议 - 图20

上面是我偶然发现的,没记住掘金域名,想用掘金举例子,结果用.com搜出来的并不是掘金,而恰巧它还是http协议的,可以看到,现在的浏览器,如果是 HTTP 协议的,就很明目的提示我们此网站是不安全的,因此我们在输入一些表单私人信息的时候就需要注意了。而第二个图则是掘金的,很明显旁边有个小锁头,看起来就很稳妥有安全感~

出上面两种外,其实还有其他状态比如HTTPS域名下引用了部分 HTTP 内容,那么就会是另一种状态,如下图所示:(一来说,如果是公司项目,最好将网络的图片和 js 脚本放到自己的域名下)

http(s)协议 - 图21

HTTPS 本质也是基于 HTTP 协议的,不过通过一些安全手段来解决上面 HTTP 存在的问题。它的通用接口部分使用 SSL(Secure Socket Layer) 和 TLS(Transport Layer Security) 协议代替。以前来说,HTTP 协议是应用层协议,直接和下层 TCP 进行通信,而增加了 SSL 协议之后,就变成了 HTTP 先跟 SSL 通信再由 SSL 跟 TCP 通信,也就是说HTTPS是披着 SSL 协议外壳的 HTTP 协议。

http(s)协议 - 图22

SSL 是独立于 HTTP 的协议,所以不光是 HTTP 协议,其他运行在应用层的 SMTP 和 Telnet 等协议均可配合 SSP 协议使用。可以说 SSL 是当今世界上应用最为广泛的网络安全技术。

简单来说 HTTPS = HTTP + 加密 + 证书 + 完整性保护

加密

HTTPS 采用混合加密机制,也就是对称加密与非对称加密混用来实现加密机制

数据传输阶段(对称密钥加密)

对称密钥加密又称为共享密钥加密(Common key crypto system),是在加密和解密阶段使用同一个密钥的方式。密钥在传递途中可能会被黑客窃取,那他就可以在之后随意解密收发的数据,通信过程也就没有机密性可言了。

http(s)协议 - 图23

因此,加密的重中之重就是 如何安全地发送密钥并不泄漏

证书交换验证阶段(非对称加密)

公开密钥加密(Public-key cryptography)解决了上述的发送密钥问题。它采用一对非对称的密钥,一把公钥一把私钥。加密过程就是,发送加密报文的一方是用对方的公开密钥进行加密,接收方式用自己本地的私钥进行解密,也就是说发送方并不需要附带着发送用来解密的密钥,这种方式就不需要考虑密钥在传输过程中被攻击这获取到。

私钥和公钥是一对多的关系,公钥可以随意转发,只要采用公钥加密的报文,都只能使用对应私钥进行解密。

混合验证机制

HTTPS 采用的是混合加密。

原因是,非对称加密相比对称加密更加复杂,效率更低,在前端业务中一般都是存在大量的 HTTP 请求,所以非对称加密的低效是无法被接受的。此外,非对称加密的场景只在服务端保存私钥,也就是说一对公私钥只能单向传输数据,因此可以用来确认通信安全以及服务端返回证书。确认安全之后,传输数据采用的就是速度更快的对称加密。

在通信刚开始的时候使用非对称算法,比如 RSA、ECDHE,首先解决密钥交换的问题。

对方拿到密文后用私钥解密,取出会话密钥。这样,双方就实现了对称密钥的安全交换,后续就不再使用非对称加密,全都使用对称加密。

http(s)协议 - 图24

证书

公钥事先植入到浏览器系统,

上面的过程也存在一个问题,安全的本质是使用密钥进行加密。但是如果密钥本身就有问题,那么安全也就无从谈起,因此这个密钥必须是通信双方认可的。这个工作不能交给客户端做,也不能服务端做,一半交给第三方权威机构 — 数字证书认证机构(CA,Certificate Authority)。

http(s)协议 - 图25

认证机关的公开密钥必须安全地转交给客户端,使用通信方式是,如何安全转交是一件很困难的事,因此多数浏览器发布版本时,都会是现在内部置入常用认证机关的公钥。

数据完整性

确保数据完整性,也就意味着数据安全没有被第三方篡改,这时候就需要通过 数字签名

数字签名

数字签名是一段由发送者生成的特殊加密校验码,用于传输过程中确认报文的完整性。数字签名涉及到了两种技术:非对称加密数字摘要。生成数字摘要的算法通过 MD5SHA 这种不可逆算法,将不定长的报文内容提取出定长的数字摘要。

数字签名的整个签名和校验过程分为五步:

  1. 发送方用摘要算法对报文提取生成数字摘要
  2. 使用私钥对摘要进行加密,加密后的摘要作为数字签名附加在报文上一起发送给接收方
  3. 接收方收到报文后,使用相同的摘要算法提取出摘要
  4. 在使用公钥对报文的数字签名进行解密
  5. 如果解密后的数字签名与提取出的摘要相同,那么说明报文没有被篡改,数据是完整的

多说一句,对于本地存储,无论是服务端的私钥还是客户端的随机数,都不是 HTTPS 通信过程的安全考虑,HTTPS 只保证在网络传输过程的数据安全性,本地的内容安全不被窃取依靠的是防火墙,杀毒软件等等。

http(s)协议 - 图26

完整流程

证书验证阶段

  1. 客户端发起 HTTPS 请求
  2. 服务端返回 HTTPS 证书
  3. 客户端验证证书是否合法,不合法则提示警告

数据传输阶段

  1. 当证书验证合法后,在本地生成随机密码串
  2. 通过公钥加密随机密码串,并把加密后的随机密码串传输到服务端
  3. 服务端通过私钥对随机密码串进行解密
  4. 服务端通过客户端传入的随机密码串构建对称加密算法,对返回的结果内容进行加密后传输

二十二、HTTPS 并不全是优点

速度慢

HTTPS 速度会相比 HTTP 慢 2~100 倍

HTTPS 慢其实是慢在 SSL 协议通信商上,因为 SSL 协议要进行加密解密处理,会占用 CPU 和网络资源,总体会慢一些

http(s)协议 - 图27

CA 证书一般不免费

申请 CA 证书是需要花钱的,当然有很多手段可以申请免费的 HTTPS 证书,但是大部分权威机构还是需要收费的。所以对于大部分小型开发者来说,使用 HTTP 协议更划算。

相关题目

为什么 HTTP 不安全

  1. 报文是明文的,没有加密
  2. 无法验证报文的完整性,传输过程中可能会被篡改
  3. 不验证通信双方的身份,可能会被伪装

为什么 HTTPS 是安全的

HTTPS = HTTP + 加密 + 证书 + 完整性保护。

相关安全操作是通过 SSL 协议来进行实现的。

非对称加密为什么慢,非对称加密除了慢外还有什么缺点?

非对称加密基于大数运算,比如大素数或者椭圆曲线,是复杂的数学难题,所以消耗计算量,运算速度慢。

除了慢,可能还有一个缺点就是需要更多的位数,相同强度的对称密钥要比非对称密钥短。

对称密钥一般都128位、256位,而rsa一般要2048位,不过椭圆曲线的会短一点。

HTTPS 绝对安全吗

并不是,HTTPS 也会被抓包,只不过内容被加密过。不过用户可以主动对证书进行授权,如果用户授权通过,那么代理软件是可以对传输内容进行解密的。

非对称加密为什么慢,非对称加密除了慢外还有什么缺点?

非对称加密基于大数运算,比如大素数或者椭圆曲线,是复杂的数学难题,所以消耗计算量,运算速度慢。

除了慢,可能还有一个缺点就是需要更多的位数,相同强度的对称密钥要比非对称密钥短。

对称密钥一般都128位、256位,而rsa一般要2048位,不过椭圆曲线的会短一点。

二十三、HTTPS 握手过程

  1. 服务端将自己的公钥登录至数字证书认证机构,数字证书认证机构用自己的私钥对服务端公钥署数字签名;
  2. 客户端发出 HTTPS 请求,请求服务端建立 SSL / TLS 连接;
  3. 服务端接收到 HTTPS 请求,将申请到的数字证书和服务端公钥一同返回给客户端;
  4. 客户端在接收到服务端公钥后,数字证书认证机构利用提前植入到浏览器的认证公钥,向数字证书认证机构认证公钥证书上的数字签名,确认服务器公钥的真实性;
  5. 认证通过之后,客户端随机生成通信使用的密钥,然后使用服务端公钥对密钥进行加密,返回给服务端;
  6. 服务端收到加密内容后,通过服务端私钥进行非对称解密,得到客户端密钥,至此双方都获得了对称加密的密钥;
  7. 之后,双方使用密钥进行对称加密通信。

二十四、http三次握手四次挥手

三次握手过程理解

http(s)协议 - 图28

第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

四次挥手过程理解

http(s)协议 - 图29

1)客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
2)服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
4)服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
5)客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
6)服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

问题

【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,”你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

【问题3】为什么不能用两次握手进行连接?

答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。

  1. 现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机SC之间的通信,假定CS发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,CS的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。

【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

二十五、HTTP2特性

1.兼容 HTTP/1

“语义”上与 HTTP/1 完全一致,。比如请求方法、URI、状态码、头字段等概念都保留不变,这样就消除了再学习的成本,基于 HTTP 的上层应用也不需要做任何修改,可以无缝转换到 HTTP/2。

2.头部压缩

开发了专门的“HPACK”算法,减小HTTP报文中头部字段的开销,提供通信效率。采用报文头压缩主要是两个原因:

(1)对于单个HTTP报文而言,当携带较少的通信数据时,报文头部大小将远远大于有效的通信数据,导致带宽利用率较低。

(2)在持久化连接下,传送的多个HTTP报文之间,经常存在重复报文头字段在传输。

3.二进制格式

原来使用纯文本的时候容易出现多义性,比如大小写、空白字符、回车换行、多字少字等等,程序在处理时必须用复杂的状态机,效率低,还麻烦。

二进制里只有“0”和“1”,可以严格规定字段大小、顺序、标志位等格式,“对就是对,错就是错”,解析起来没有歧义,实现简单,而且体积小、速度快,做到“内部提效”。

消息不再是“Header+Body”的形式,而是分散为多个二进制“帧”。

4.虚拟的“流”,实现了多路复用

“流”是二进制帧的双向传输序列,每一个 request帧, 对应一个唯一的流 id,这样一个连接上可以有多个 request,每个连接的 request 可以随机的混杂在一起,接收方可以根据 request 的 id 将 request 再归属到各自不同的服务端请求里面。

多个请求 / 响应之间没有了顺序关系,不需要排队等待,也就不会再出现“队头阻塞”问题,降低了延迟,同时实现了“多路复用”,大幅度提高了连接的利用率。

多路复用多个请求没有顺序,而长连接多个请求必须排队,就会队头阻塞。

http/1里的请求都是排队处理的,所以有队头阻塞。

http/2的请求是乱序的,彼此不依赖,所以没有队头阻塞。

5.强化安全

要求至少是 TLS1.2,而且禁用了很多不安全的密码套件。

对于 队头阻塞问题,只要传输层是TCP,就不会得到根本上的解决, http/2 利用流的的机制很大程度的缓解了这个问题,http/3 传输层换成了 UDP 才彻底解决这个问题

二十六、HTTP/2内核剖析

1.连接前言

由于 HTTP/2“事实上”是基于 TLS,所以在正式收发数据之前,会有 TCP 握手和 TLS 握手,TLS 握手成功之后,客户端必须要发送一个“连接前言”(connection preface),用来确认建立 HTTP/2 连接。关键字是“PRI”

  1. PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n

服务器收到这个“有魔力的字符串”,就知道客户端在 TLS 上想要的是 HTTP/2 协议,而不是其他别的协议,后面就会都使用 HTTP/2 的数据格式。

2.头部压缩

基于静态字典压缩

在HTTP协议中的客户端以及服务端之间,共同维护了一份静态字典。该静态字典中存储了大量常见的HTTP报文头字段。

基于动态字典压缩

静态字典并不能够涵盖HTTP头部键值对所有的组合情况,为此在静态字典压缩的基础上补充了动态字典压缩。

动态字典压缩过程比较简单。如果遇见在静态字典中不存在的HTTP头部字段,那么此处采用非压缩传输,接着把该头部字段添加到动态字段中。当下次传送同样的头部字段时,则可以依据动态字典的内容对该头部字段进行压缩了。

3.二进制帧

4.流与多路复用

  • 流是可并发的,一个 HTTP/2 连接上可以同时发出多个流传输数据,也就是并发多请求,实现“多路复用”;
  • 客户端和服务器都可以创建流,双方互不干扰;
  • 流是双向的,一个流里面客户端和服务器都可以发送或接收数据帧,也就是一个“请求 - 应答”来回;
  • 流之间没有固定关系,彼此独立,但流内部的帧是有严格顺序的;
  • 流可以设置优先级,让服务器优先处理,比如先传 HTML/CSS,后传图片,优化用户体验;
  • 流 ID 不能重用,只能顺序递增,客户端发起的 ID 是奇数,服务器端发起的 ID 是偶数;
  • 在流上发送“RST_STREAM”帧可以随时终止流,取消接收或发送;
  • 第 0 号流比较特殊,不能关闭,也不能发送数据帧,只能发送控制帧,用于流量控制。

总结:

  • HTTP/2 必须先发送一个“连接前言”字符串,然后才能建立正式连接;
  • HTTP/2 废除了起始行,统一使用头字段,在两端维护字段“Key-Value”的索引表,使用“HPACK”算法压缩头部;
  • HTTP/2 把报文切分为多种类型的二进制帧,报头里最重要的字段是流标识符,标记帧属于哪个流;
  • 流是 HTTP/2 虚拟的概念,是帧的双向传输序列,相当于 HTTP/1 里的一次“请求 - 应答”;
  • 在一个 HTTP/2 连接上可以并发多个流,也就是多个“请求 - 响应”报文,这就是“多路复用”。

二十七、WebSocket,全双工通信

“WebSocket”是一种基于 TCP 的轻量级网络通信协议,在地位上是与 HTTP“平级”的。

1.为什么要有 WebSocket

HTTP/2 针对的是“队头阻塞”,传输效率低下的问题,而 WebSocket 针对的是“请求 - 应答”通信模式。

“请求 - 应答”是一种“半双工”的通信模式,虽然可以双向收发数据,但同一时刻只能一个方向上有动作,传输效率低。更关键的一点,它是一种“被动”通信模式,服务器只能“被动”响应客户端的请求,无法主动向客户端发送数据。

虽然后来的 HTTP/2、HTTP/3 新增了 Stream、Server Push 等特性,但“请求 - 应答”依然是主要的工作方式。这就导致 HTTP 难以应用在动态页面、即时消息、网络游戏等要求“实时通信”的领域。

2.WebSocket 的特点

2.1 全双工

WebSocket 是一个真正“全双工”的通信协议,客户端和服务器都可以随时向对方发送数据。

一旦后台有新的数据,就可以立即“推送”给客户端,不需要客户端轮询,“实时通信”的效率也就提高了。

2.2 二进制帧结构

WebSocket 虽然有“帧”,但却没有像 HTTP/2 那样定义“流”,也就不存在“多路复用”“优先级”等复杂的特性,而它自身就是“全双工”的,也就不需要“服务器推送”。

2.3WebSocket 的握手

WebSocket 的握手是一个标准的 HTTP GET 请求,但要带上两个协议升级的专用头字段和两个认证用头字段:

  • “Connection: Upgrade”,表示要求协议“升级”;
  • “Upgrade: websocket”,表示要“升级”成 WebSocket 协议。
  • Sec-WebSocket-Key:一个 Base64 编码的 16 字节随机数,作为简单的认证密钥;
  • Sec-WebSocket-Version:协议的版本号,当前必须是 13。

服务器收到 HTTP 请求报文,看到上面的四个字段,就知道这不是一个普通的 GET 请求,而是 WebSocket 的升级请求,于是就不走普通的 HTTP 处理流程,而是构造一个特殊的“101 Switching Protocols”响应报文,通知客户端,接下来就不用 HTTP 了,全改用 WebSocket 协议通信。

3.WebSocket 与 HTTP/2 的异同点

同:

  • 都可以从 HTTP/1 升级,都采用二进制帧结构。

异:

  • HTTP/2是请求与响应的模式,在WebSocket是“全双工”,没有请求响应的概念,服务器也可以主动向客户端发起请求,收发的都是数据帧。
  • websocket里面有帧的概念,却没有http2.0里的虚拟流的概念,也不存在优先级、多路复用。
  • websocket的出现本质上还是为了解决http的半双工的问题,变成全双工,服务器和客户端可以随意通行的问题。

工作场景遇到过用户订阅股票的股价,股价波动时实时推送给海量订阅的用户,面试场景被问到两次,一 千万粉丝的明星发布动态如何推送给粉丝 二 海量用户的主播直播如何推送弹幕 当时回答消息队列,其实web socket才是比较好的方案。 WebSocket适合实时通信交互的场景,和消息队列其实是两个领域,不冲突,可以互相结合使用。

二十八、WAF:保护网络服务

1.Web 服务遇到的威胁

DDoS”攻击

黑客会控制许多“僵尸”计算机,向目标服务器发起大量无效请求。服务器无法区分正常用户和黑客,只能“照单全收”,这样就挤占了正常用户所应有的资源。

代码注入

HTTP 报文在语义结构上非常松散、灵活,URI 里的 query 字符串、头字段、body 数据都可以任意设置,这就带来了安全隐患,给了黑客“代码注入”的可能性。

SQL 注入

“SQL 注入”(SQL injection)应该算是最著名的一种“代码注入”攻击了,它利用了服务器字符串拼接形成 SQL 语句的漏洞,构造出非正常的 SQL 语句,获取数据库内部的敏感信息。

HTTP 头注入

“HTTP 头注入”,在“Host”“User-Agent”“X-Forwarded-For”等字段里加入了恶意数据或代码,服务端程序如果解析不当,就会执行预设的恶意代码。

跨站脚本”(XSS)攻击

“跨站脚本”(XSS)攻击,它属于“JS 代码注入”,利用 JavaScript 脚本获取未设防的 Cookie。

2.网络应用防火墙

传统“防火墙”工作在三层或者四层,隔离了外网和内网,使用预设的规则,只允许某些特定 IP 地址和端口号的数据包通过,拒绝不符合条件的数据流入或流出内网,实质上是一种网络数据过滤设备。

WAF 也是一种“防火墙”,但它工作在七层,看到的不仅是 IP 地址和端口号,还能看到整个 HTTP 报文,所以就能够对报文内容做更深入细致的审核,使用更复杂的条件、规则来过滤数据。

3.WAF的功能

通常一款产品能够称为 WAF,要具备下面的一些功能:

  • IP 黑名单和白名单,拒绝黑名单上地址的访问,或者只允许白名单上的用户访问;
  • URI 黑名单和白名单,与 IP 黑白名单类似,允许或禁止对某些 URI 的访问;
  • 防护 DDoS 攻击,对特定的 IP 地址限连限速;
  • 过滤请求报文,防御“代码注入”攻击;
  • 过滤响应报文,防御敏感信息外泄;审计日志,记录所有检测到的入侵操作。

二十九、 CDN:加速我们的网络服务

由于客观地理距离的存在,直连网站访问速度会很慢,所以就出现了 CDN;

CDN的目的是 构建全国、全球级别的专网,让用户就近访问专网里的边缘节点,降低了传输延迟,实现了网站加速;

1.什么是 CDN?

CDN 的最核心原则是“就近访问”,使用“缓存代理”技术,把源站的内容逐级缓存到网络的每一个节点上。用户在上网的时候就不直接访问源站,而是访问离他“最近的”一个 CDN 节点。

那么,CDN 都能加速什么样的“内容”呢?

只有静态资源才能够被缓存加速、就近访问,而动态资源只能由源站实时生成,即使缓存了也没有意义。不过,如果动态资源指定了“Cache-Control”,允许缓存短暂的时间,那它在这段时间里也就变成了“静态资源”,可以被 CDN 缓存加速。

2.CDN 的负载均衡

全局负载均衡(Global Sever Load Balance)一般简称为 GSLB,它是 CDN 的“大脑”,主要的职责是当用户接入网络的时候在 CDN 专网中挑选出一个“最佳”节点提供服务,解决的是用户如何找到“最近的”边缘节点,对整个 CDN 网络进行“负载均衡”。

2.1 全局负载均衡的实现方式

最常见的实现方式是“DNS 负载均衡”

原来没有 CDN 的时候,权威 DNS 返回的是网站自己服务器的实际 IP 地址,浏览器收到 DNS 解析结果后直连网站。但加入 CDN 后就不一样了,权威 DNS 返回的不是 IP 地址,而是一个 CNAME( Canonical Name ) 别名记录,指向的就是 CDN 的 GSLB。因为没拿到 IP 地址,本地 DNS 就会向 GSLB 再发起请求,这样就进入了 CDN 的全局负载均衡系统,开始“智能调度”,主要的依据有这么几个:

  • 看用户的 IP 地址,查表得知地理位置,找相对最近的边缘节点;
  • 看用户所在的运营商网络,找相同网络的边缘节点;
  • 检查边缘节点的负载情况,找负载较轻的节点;
  • 其他,比如节点的“健康状况”、服务能力、带宽、响应时间等。

GSLB 把这些因素综合起来,用一个复杂的算法,最后找出一台“最合适”的边缘节点,把这个节点的 IP 地址返回给用户,用户就可以“就近”访问 CDN 的缓存代理了。

3.CDN 的缓存代理

把源站的内容逐级缓存到网络的每一个节点上。用户在上网的时候就不直接访问源站,而是访问离他“最近的”一个 CDN 节点。

缓存系统只能有选择地缓存那些最常用的静态资源

对于动态资源,如果动态资源指定了“Cache-Control”,就允许缓存短暂的时间,如果不允许缓存,就只能回源。

cdn一般有专用的高速网络直连源站,或者是动态路径优化,所以动态资源回源要比通过公网速度快很多。

三十、CDN加速原理

简单来说,CDN 的工作原理就是将您源站的资源缓存到位于全球各地的 CDN 节点上,用户请求资源时,就近返回节点上缓存的资源,而不需要每个用户的请求都从您的源站获取,避免网络拥塞、缓解源站压力,保证用户访问资源的速度和体验。

http(s)协议 - 图30

CDN 对网络的优化作用主要体现在如下几个方面:

  • 解决服务端的 第一公里 的问题
  • 缓解甚至消除不同运营商之间互联的瓶颈造成的影响
  • 减轻了各省的出口带宽压力
  • 缓解了骨干网的压力
  • 优化了网上热点内容

工作原理

传统访问过程

http(s)协议 - 图31

由上图可见,用户访问未使用 CDN 缓存网站的过程:

  1. 用户输入访问的域名,操作系统向 LocalDNS 查询域名的 IP 地址
  2. LocalDNS 向 Root DNS 查询域名的授权服务器(这里假设 LocalDNS 的缓存已过期)
  3. Root DNS 将域名授权 DNS 记录回应给 LocalDNS
  4. LocalDNS 得到域名授权的记录后,继续向域名授权 DNS 查询域名的 ip 地址
  5. LocalDNS 将得到的域名 ip 地址返回给客户端
  6. 用户得到域名IP地址后,访问站点服务器
  7. 站点服务器响应请求,将请求资源返回给客户端

CDN 访问过程

http(s)协议 - 图32

通过上图,我们可以了解到,使用了 CDN 缓存后的网站访问过程变更为:

  1. 用户输入访问的域名,操作系统向 LocalDNS 查询域名的IP地址
  2. LocalDNS 向 RootDNS 查询域名的授权服务器(这里假设缓存已过期)
  3. RootDNS 将域名授权 DNS 记录返回给 LocalDNS
  4. LocalDNS 得到域名的授权记录后,继续向域名授权 DNS 查询域名的IP地址
  5. 域名授权DNS查询域名记录后(一般是CNAME),回应给 LocalDNS
  6. 智能调度 DNS 根据一定的算法和策略(比如静态拓扑、容量等)将最合适的 CDN 节点IP地址回应给 LocalDNS
  7. LocalDNS 得到域名IP地址,返回给客户端
  8. 客户端得到IP地址后发送请求访问站点服务器
  9. CDN 节点服务器应答请求,将内容返回给客户端

通过以上的分析,我们可以得到:为了实现对普通用户透明(使用缓存后用户无需进行任何设置)访问,需要使用 DNS(域名解析)来引导用户来访问 Cache 服务器,以实现透明的加速服务。由于用户访问网站的第一步就是域名解析,所以通过修改 DNS 来引导用户访问是最简单有效的方式。

组成要素

对于普通的互联网用户,每个 CDN 节点就相当于一个放置在他周围的网站服务器。通过对 DNS 的接管,用户的请求被透明的指向里他最近的节点,节点中的 CDN 服务器会想网站的原始服务器一样,响应用户的请求。由于它离用户最近,所以响应时间也是更快。

从上面的图中我们可以得知虚线圈起来那块就是 CDN层,这层是位于用户端和站点服务器之间。

  • 智能调度 DNS
    智能调度 DNS 是 CDN服务中的关键系统,当用户访问加入 CDN 服务的网站时,域名解析请求将最终由 智能调度 DNS 负责处理。它通过一组预先定义好的策略,将当时最接近用户的节点地址提供给用户,使用户可以得到快速的服务。同时它需要与分布在各地的 CDN 节点保持童心,跟踪个节点的健康状态、容量等信息,确保将用户的请求分配到就近可用的节点上。
  • 缓存功能服务
    负载均衡设备
    内容 Cache 服务器
    共享存储

名词解释

CNAME记录(CNAME record)

CNAME 即别名(Canonical Name);可以用来把一个域名解析成另一个域名,当 DNS 系统在查询 CNAME 左边的名称的时候,都会转向 CNAME 右边的名称在进行查询,一直最总到最后的 PTR 或 A名称,成功查询才会做出回应,否则失败。

例如,你有一台服务器上存放了很多资料,你是用 doc.example.com 去访问这些资源,但又希望通过 documents.example.com 也能访问到这些资源,那么你就可以在你的 DNS 解析服务商添加一条 CNAME 记录,将 documents.example.com 指向 doc.example.com ,添加记录之后,所有访问 documents.example.com 的请求都会被转到 doc.example.com ,并得到相同的响应。

CNAME域名

接入 CDN 时,在 CDN 提供商控制台添加完加速域名后,您会得到一个 CDN 给您分配的 CNAME域名,您需要在您的 DNS 解析服务上添加 CNAME 记录,将自己的加速域名指向这个 CNAME域名,这样该域名的请求才会指向 CDN 的节点,以达到加速效果。

回源host

回源host 决定回源请求访问到原站上的具体站点。

例子1:源站是域名源站为 www.a.com ,回源host为 www.b.com ,那么实际回源是请求到 www.a.com 解析到的IP,对应的主机上的站点 www.b.com 例子2:源站是IP源站为 1.1.1.1,回源host为 www.b.com,那么实际回源的是 1.1.1.1 对应的主机上的站点 www.b.com

协议回源

指回源时使用的协议和客户端访问资源时的协议保持一致,即如果客户端使用 HTTPS 方式请求资源,当CDN节点上未缓存该资源时,节点会使用相同的 HTTPS 方式回源获取资源;同理如果客户端使用 HTTP 协议的请求,CDN节点回源时也使用HTTP协议。

三十一、TCP和UDP的区别

  1. TCP 是面向连接的。(就好像打电话一样,通话前需要先拨号建立连接,通话结束后要挂机释放连接);
  2. 每一条 TCP 连接只能有两个端点,每一条 TCP 连接只能是点对点的(一对一);
  3. TCP 提供可靠交付的服务。通过 TCP 连接传送的数据,无差错、不丢失、不重复、并且按序到达;
  4. TCP 提供全双工通信。TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接收缓存,用来临时存放双方通信的数据;
  5. 面向字节流。TCP 中的“流”(Stream)指的是流入进程或从进程流出的字节序列。“面向字节流”的含义是:虽然应用程序和 TCP 的交互是一次一个数据块(大小不等),但 TCP 把应用程序交下来的数据仅仅看成是一连串的无结构的字节流。
  6. UDP 是无连接的;
  7. UDP 使用尽最大努力交付,即不保证可靠交付,因此主机不需 要维持复杂的链接状态(这里面有许多参数);
  8. UDP 是面向报文的;
  9. UDP 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如 直播,实时视频会议等);
  10. UDP 支持一对一、一对多、多对一和多对多的交互通信;
  11. UDP 的首部开销小,只有 8 个字节,比 TCP 的 20 个字节的首部要短。

TCP 的可靠体现在 TCP 在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源 。

在某些情况下 UDP 确是一种最有效的工作方式(一般用于即时通信),比如:QQ 语音、 QQ 视频 、直播等等。

https://www.cnblogs.com/lyt0207/p/12484262.html

三十二、http1.0 、2.0的区别

一丶HTTP1.0与HTTP1.1通信性能上的区别

  1. 持久化连接
  2. 管线化技术

二丶HTTP2.0与HTTP1.1通信性能上的区别

  1. 多路复用
  2. HTTP协议头部压缩
  3. 二进制数据帧
  4. 服务端推送

正文

一丶HTTP1.0与HTTP1.1通信性能上的区别

  • 持久化连接
    HTTP1.1是默认支持持久化连接的。HTTP1.0若要支持持久化连接需要显示指定Keep-alived报文头。
    1. 非持久化连接下HTTP协议的通信
    • 比如访问www.taobao.com这个URL。访问该URL时,首先会从目标服务器上到HTML这样的静态资源,服务器返回资源后会自动断开连接,这是一次非持久的HTTP通信过程。在该过程中包括TCP三次握手和四次挥手。
    • 更进一步考虑,静态HTML上必然包括很多图片,js,css等资源,这些资源全部都是存储在服务器上。对这些资源的访问会重复上述的HTTP通信过程,其中又包括了TCP三次握手和四次挥手。这种反复建立和释放TCP连接的过程无疑浪费了服务器很多的带宽资源,也降低了Web页面的加载速度。非持久化连接下HTTP协议的通信过程如下图所示

http(s)协议 - 图33
2. 持久化连接下HTTP协议的通信
持久化连接很易懂。在一次HTTP通性过程后,服务器若没有受到显示关闭连接的通知其不会断开连接,而是一直保持该连接。如此一来,在访问诸如www.taobao.com这样的页面时,页面上的多数资源能够在一条TCP链接上传输。这样极大的减少了多次TCP连接,释放带来的性能损失。持久化连接下的通信如下图所示
http(s)协议 - 图34

  • 管线化技术

管线化技术是在持久化连接的基础上,进一步对通信性能的提升。在持久化连接下,请求和相应是顺次进行的。上次请求得到响应后,才能发送下次请求。管线化技术就是指能在未收到响应时,顺次发送多个响应。
http(s)协议 - 图35

二丶HTTP2.0与HTTP1.1之间通信性能对比

  1. 多路复用技术
    多路复用技术建立在持久连接的基础上,允许所有请求公用同一连接,并且能够并行传输。此处的多路复用技术和管线化技术值不同之处在于:。
    • 管线化技术中所有,请求是顺次发送出去的。而多路复用中,所有请求是并行发送出去的。
      http(s)协议 - 图36
  2. 报文头压缩
    报文头压缩同样比较容易理解,减小HTTP报文中头部字段的开销,提供通信效率。采用报文头压缩主要是两个原因:
    (1)对于单个HTTP报文而言,当携带较少的通信数据时,报文头部大小将远远大于有效的通信数据,导致带宽利用率较低。
    (2)在持久化连接下,传送的多个HTTP报文之间,经常存在重复报文头字段在传输。
    HTTP/2 在客户端和服务端使用 “首部表” 来跟踪和存储之前发送的键值对,对于相同的数据,不再通过每次请求和响应发送;
    http(s)协议 - 图37
    1. 当通信过程越长导致动态字典积累的内容将越多,因此HTTP头部压缩的效果越佳
    2. 动态字典的内容会在连接新建立的时候重置。
  3. 二进制数据帧
    HTTP/2 采用二进制格式传输数据,而非 HTTP/1.x 的文本格式,二进制协议解析起来更高效。
  4. 服务端推送
    服务端可以在发送 HTML 时主动推送其他资源,而不是等浏览器解析到相应位置,发起请求再响应。例如服务端可以主动把 JS、CSS 文件推送给客户端,而不需要客户端解析 HTML 时在发送这些请求。
    服务端可以主动推送,客户端也有权利选择是否接受。如果服务端推送的资源已经被浏览器缓存过,浏览器可以通过发送 RST_STREAM 帧来拒收。主动推送也遵守同源策略,服务器不会随便推送第三方资源给客户端。

三十四、鉴权

前后端鉴权是一个很大的话题,不同组织的鉴权方式各不相同,甚至对同一协议的业务实现也可能相去甚远。

文章主要包含三个部分:

  • 区分认证和授权
  • 常见的认证及授权方式
  • 企业应用中常见的单点登录(SSO)方案

认证与授权

首先我们来简单看一下认证和授权的区别,理清楚二者之间的关系。

认证(Authentication)

认证涉及一方应用和一方客户,用于描述客户在该应用下的身份。认证可以简单理解为登录,以此确认你是一个合法的用户。比如掘金就必须登录才能点赞评论收藏。

授权(Authorisation)

授权涉及两方应用和一方客户,用于描述第三方应用有哪些操作权限。

代入场景区分认证和授权

只认证不授权

上面我们使用掘金账号登录掘金就是只认证不授权,此时掘金只知道你是哪个用户,但是不涉及到授权操作。

既认证又授权

同样是登录掘金,我们可以不使用掘金账号进行登录,而是选择第三方应用登陆,比如使用微信登录。这个过程就是掘金向微信申请授权,获取微信用户的信息,用以注册掘金的账户。这个过程及完成了认证(注册成为合法用户)同时又进行了授权(掘金向微信申请授权获取个人信息)。

不认证只授权

以某外卖小程序为例,在你第一次进入外卖小程序时,小程序会弹出浮层请求获取个人信息,此时相当于上面提到的既认证又授权。你同意之后就相当于使用微信登录,但是此时小程序获取到的信息并不包括你的手机号,当你要下单点击提交时,小程序再次发起请求,要获取你绑定的手机号,此时发生的动作就是不认证只授权。

有哪些常见的认证和授权方式

一旦涉及认证,就必须要考虑一个问题就是状态管理。所谓的状态管理就是我们在一个网站进行登录之后一段时间内,再次访问该网站不需要重新登录,所以开发者必须要考虑怎样保持用户的登录状态已决定何时生效何时失效。而这个过程需要前后端协同开发。

Session-Cookie 认证

流程
  1. 用户先使用用户名和密码登录
  2. 服务端拿到登录信息之后,将用户信息保存在 session 中,并把 sessionID 写到前端 cookie 里面
  3. 之后的每一次请求,前端都会带上 cookie,后端只需要通过获取 cookie 中的 sessionID,判断 sessionID 是否过期

主要问题
  • cookie 安全性问题。攻击者可以通过 xss 获取 cookie 中的 sessionID,使用 httpOnly 在一定程度上可以提高安全性
  • 过多的 session 会消耗较大的服务器资源
  • 分布式下 session 共享问题

Token 认证

与上面的 Session-Cookie 机制不同的是,基于 token 的用户认证是一种服务端无状态的认证方式,服务端可以不用存放 token 数据,但是服务端需要认证 token 的合法性和有效性。使用 token 进行认证的方式这里主要介绍两种:SAML 和 JWT。

三十五、cookie

2 月份发布的 Chrome 80 版本中默认屏蔽了第三方的 Cookie,这导致了线上非常多的问题,着实推动了大家对 Cookie 的理解,所以很有可能会有相关的面试题,即便不是面试题,当问到 HTTP 相关内容时,不妨也扯到这件事情上,一能表明你对前端时事的跟进,二能借此引申到前端安全方面的内容,为你的面试加分。

本文就给大家介绍一下浏览器的 Cookie 以及这个“火热”的 SameSite 属性。

HTTP

一般我们都会说「HTTP 是一个无状态协议」,不过要注意这里的 HTTP 其实指的是 HTTP/1.x,而所谓无状态协议,简单的理解便是即使同一个客户端连续两次发送请求给服务器,服务器也识别不出这是同一个客户端发送的请求,这导致的问题就是比如你加了一个商品到购物车中,但因为识别不出是同一个客户端,你刷新页面就消失了。

  • 是什么:一段 key-value 形式的文本片段,通讯时服务器可通过 http header 下发 cookie 信息记录到用户浏览器;浏览器在后续通讯时会自动带上
  • 为什么:http 本身是一种无状态协议,服务器无法单纯从网络协议层面判定用户身份,因此需要通过 cookie 方式持久化用户凭证,让服务器能映射回用户信息
  • 怎么用:
    • 可通过服务器 response 的 set-cookie 头下发;也可以在 js 中写入;后续浏览器会自动帮我们带上

      Cookie

为了解决 HTTP 无状态导致的问题,后面推出了 Cookie。

不过这样说可能会让你产生一些误解。首先无状态并不是不好,有优点,但也会导致一些问题。而 Cookie 的存在也不是为了解决通讯无状态的问题,只是为了解决客户端与服务端会话状态的问题,这个状态是指后端服务的状态而非通讯协议的状态。

Cookie 介绍

我们看一下 Cookie,引用维基百科:

Cookie,类型为「小型文本文件」,指某些网站为了辨别用户身份而储存在用户本地终端上的数据。

作为一段一般不超过 4KB 的小型文本数据,它由一个名称(Name)、一个值(Value)和其他几个用于介绍控制 Cookie 有效期、安全性、使用范围的可选属性组成,这涉及的属性我们会在后面介绍。

Cookie 的查看

我们可以在浏览器开发者工具中查看到当前页面的 Cookie,尽管我们是在浏览器中看到了 Cookie,这并不意味着 Cookie 文件只是存放在浏览器里。实际上,Cookie 相关的内容还可以存在本地文件中,就比如说 Max 下的 Chrome,存放目录就是 ~/Library/Application Support/Google/Chrome/Default ,里面会有一个名为 Cookies 的数据库文件,你可以使用 sqlite 软件打开它。

存放在本地的好处就在于即使你关闭了浏览器,Cookie 依然可以生效。

Cookie 的设置

简单来说:

  1. 客户端发送 HTTP 请求
  2. 服务器收到 HTTP 请求,在响应头里面添加一个 Set-Cookie 字段
  3. 浏览器受到相应后保存下 Cookie
  4. 之后对该服务器每一次请求中都通过 Cookie 字段将 Cookie 信息发送给服务器

Cookie 的属性

Name/Value

用 JS 操作 Cookie 的时候注意对 Value 进行编码处理。

Expires

用于设置 Cookie 的过期时间。

当 Expires 缺省的时候,表示是会话性 Cookie。当 Cookie 为会话性时,值会保存在浏览器内存中,用户关闭浏览器时会失效。需要注意的是,部分浏览器提供了会话恢复功能,这种情况下即便关闭了浏览器,会话期 Cookie 也会被保留下来,就好像浏览器从来没有关闭一样。

与会话性 Cookie 相对的是持久性 Cookie,持久性 Cookie 会保存在用户的硬盘中,直至过期或者清除 Cookie。

这里值得注意的是,设定的日期和时间只和客户端相关,而不是服务端。

Max-Age

用于设置在 Cookie 失效之前需要经过的秒数。

Max-Age 可以为正数、负数、甚至是 0.

如果为正数,浏览器会将其持久化,即写到对应的 Cookie 文件中。

如果属性为负数,表示该 Cookie 只是一个会话性 Cookie。

当为 0 时,会立即删除这个 Cookie。

如果 Expires 和 Max-Age 同时存在时,Max-Age 优先级更高。

Domain

Domain 制定了 Cookie 可以送达的主机名。假如没有指定,那么默认值为当前文档访问地址中的主机部分(但是不包含子域名)。

像淘宝首页设置的 Domain 就是 .taobao.com ,这样不论是 a.taobao.com 还是 b.taobao.com 都可以使用 Cookie。

这里需要注意的是,不能跨域设置 Cookie,这样设置是无效的。

Path

Path 指定了一个 URL 路径,这个路径必须出现在要请求的资源的路径中才可以发送 Cookie 首部。比如设置了 Path=/docs 下的资源会带 Cookie 首部, /test 则不会携带 Cookie 首部。

Domain 和 Path 标识共同定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。

Secure 属性

标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。使用 HTTPS 安全协议可以保护 Cookie 在浏览器和服务器之间传输过程不被窃取和篡改。

HTTPOnly

设置 HTTPOnly 属性可以防止客户端脚本通过 document.cookie 等方式访问 Cookie,有助于避免 XSS 攻击。

SameSite

这是一个非常值得讨论的内容。

作用

SameSite 属性可以让 Cookie 在跨站请求是不会被发送,从而可以阻止跨站请求伪造(CSRF)。

属性值

SameSite 可以有下面三种值:

  1. Strict :仅允许一方请求携带 Cookie,即浏览器只发送相同站点请求的 Cookie,即当前网页 URL 与请求目标 URL 完全一致;
  2. Lax :允许部分第三方请求携带 Cookie;
  3. None :无论是否跨站都会发送 Cookie。
    1. - strict:仅同源请求发送 cookie,但过于严格,通常不太会用(例如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub Cookie,跳转过去总是未登陆状态。)
    2. - lax:稍微宽松,也限定为仅同源请求发送 cookie,但链接跳转的 get 请求除外
    3. - none:不作限制,但要求同时设置 `Secure`
    4. - 问题:假设 `domain=.juejin.com; samesite=strict` ,在 `api.juejin.com`访问,会否发送 cookie
    5. - 问题:为什么要同源限制

之前默认是 None,Chorme80 之后默认是 Lax。

跨站和跨域

首先要理解一点就是跨站和跨域是不同的。「同站(same-site)/跨站(cross-site)」和「第一方(first-party)/第三方(third-party)」是等价的。但是与浏览器同源策略(SOP)中的「同源(same-origin)/跨域(cross-origin)」是完全不同的概念。

同源策略的同源是指两个 URL 的协议 / 主机名 / 端口一致。同源策略作为浏览器的安全基石,其「同源」判断是比较严格的。

而相对来说,Cookie 中的「同站」判断就比较宽松:只要两个 URL 的 eTLD + 1 相同即可,不需要考虑协议和端口。其中 eTLD 表示有效顶级域名,注册于 Mozilla 维护的公共后缀列表(Public Suffix List)中,例如:.com.cn 等等,eTLD + 1 表示有效顶级域名 + 二级域名,例如: taobao.com 等。

举个例子,www.baidu.comwww.taobao.com 是跨站, www.a.taobao.comwww.b.taobao.com 是同站, a.github.iob.github.io 是跨站。

改变

接下来看下从 None 改成 Lax 到底影响了哪些地方的 Cookie 发送:

请求类型 实例 以前 Strict Lax None
链接 `` 发送 cookie 不发送 发送 cookie 发送 cookie
预加载 `` 发送 cookie 不发送 发送 cookie 发送 cookie
get 表单 `` 发送 cookie 不发送 发送 cookie 发送 cookie
post 表单 `` 发送 cookie 不发送 不发送 发送 cookie
iframe `` 发送 cookie 不发送 不发送 发送 cookie
AJAX $.get("...") 发送 cookie 不发送 不发送 发送 cookie
Image `` 发送 cookie 不发送 不发送 发送 cookie

从表格中可以看到,对大部分 web 应用而言,Post 表单、iframe、ajax、image 这四种情况从跨站会发送第三方 Cookie 变成了不发送。

Cookie 的作用

  1. 会话状态管理(如用户登录状态、购物车、游戏分数或其他需要记录的信息);
  2. 个性化设置(如用户自定义设置、主题等);
  3. 浏览器行为跟踪(如跟踪分析用户行为等)。

    面试题部分

    image.png

    一、HTTP 各版本

HTTP/1.0 和 HTTP/1.1 有什么区别

长连接

HTTP/1.1 支持长连接,在一个 TCP 连接上可以传送多个 HTTP 请求,避免了因为多次建立 TCP 连接的时间消耗和延时。

管线化

管线化技术是在持久化连接的基础上,进一步对通信性能的提升。在持久化连接下,请求和相应是顺次进行的。上次请求得到响应后,才能发送下次请求。管线化技术就是指能在未收到响应时,顺次发送多个请求。

http(s)协议 - 图39

缓存处理

HTTP/1.1 新增了 ETagCache-control 等新的请求头来控制缓存

HTTP 1.0 版本规定响应头字段 Expires,它对应一个未来的时间戳。

带宽优化以及网络连接的使用

HTTP/1.1 在请求头中引入了 range,支持断点续传的功能

Host 头处理

在 HTTP/1.0 中认为每台服务器都有唯一的 IP 地址,但随着虚拟主机技术的发展,多个主机共享一个 IP 地址越发普遍,HTTP/1.1 的请求消息和响应消息都应该支持 Host 头域,且请求消息中如果没有 Host 头域会报 400 错误。Host头域指定请求资源的Intenet主机和端口号,必须表示请求url的原始服务器或网关的位置

HTTP/1.1 和 HTTP/2.0 有什么区别

二进制分帧

  • :HTTP/2 数据通信的最小单位消息,指的是 HTTP/2 中逻辑上的 HTTP 消息,例如请求和响应等,消息由一个或多个帧组成。
  • :存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一的整数 ID

HTTP/2 采用二进制格式传输数据,而非 HTTP/1.x 的文本格式,二进制协议解析起来更高效。

头部压缩

HTTP/1.x 会在请求和响应中重复地携带不常改变的、冗长的头部数据,给网络带来额外的负担。

  • HTTP/2 在客户端和服务端使用 “首部表” 来跟踪和存储之前发送的键值对,对于相同的数据,不再通过每次请求和响应发送;
  • 首部表在 HTTP/2 的连接存续期内始终存在,有客户端和服务端共同渐进地更新;
  • 每个新的首部键值对要么被追加到当前表的末尾,要么替换表中的值。

你可以理解为只发送差异数据,而不是全部数据,从而减少头部的信息量。

http(s)协议 - 图40

服务端推送

服务端可以在发送 HTML 时主动推送其他资源,而不是等浏览器解析到相应位置,发起请求再响应。例如服务端可以主动把 JS、CSS 文件推送给客户端,而不需要客户端解析 HTML 时在发送这些请求。

服务端可以主动推送,客户端也有权利选择是否接受。如果服务端推送的资源已经被浏览器缓存过,浏览器可以通过发送 RST_STREAM 帧来拒收。主动推送也遵守同源策略,服务器不会随便推送第三方资源给客户端。

多路复用

HTTP/1.x 中,如果想并发多个请求,必须使用多个 TCP 连接。但浏览器为了控制资源,还会对单个域名有 6-8 个TCP 连接的请求限制,同时当带宽不足时,多个 tcp 还会出现竞争带宽的情况。

  1. 多路复用技术建立在持久连接的基础上,允许所有请求公用同一连接并且能够并行传输。此处的多路复用技术和管线化技术值不同之处在于:
    • 管线化技术中所有,请求是顺次发送出去的。而多路复用中,所有请求是并行发送出去的。
      http(s)协议 - 图41

HTTP/2 中:

  • 同域名下所有通信都在单个连接中完成;
  • 单个连接可以承载任意数量的双向数据流;
  • 数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装

[http(s)协议 - 图42

HTTP/3

HTTP/2 的缺陷

TCP 的队头阻塞

http请求应答模型导致的,如果前面的请求没有收到响应,那么后面的请求都会排队等候,造成了阻塞。

在 TCP 传输过程中,由于单个数据包的丢失而造成的阻塞称为 TCP 上的队头阻塞。HTTP/2 只解决了应用层面的队头阻塞,队头阻塞的问题还存在于 TCP 协议本身。

TCP 建立连接的延时

TCP 以及 TCP + TLS 建立连接的所产生的延时也是影响传输效率的一个主要因素。

TCP 协议僵化

中间件僵化

我们把互联网各处搭建的设备叫做中间设备(中间件),比如路由器、NAT、防火墙、交换机等,它们通常以来一些很少升级的软件,这些软件大量使用 TCP 特性,设置之后便很少进行更新。这就对我们更新 TCP 的时候造成了困难,新协议的数据包经过这些中间件时,它们不会去理解包的内容从而丢弃了这些数据包。

操作系统

因为 TCP 协议都是通过操作系统来实现的,应用程序只能使用不能修改,通常操作系统的更新都滞后于软件的更新,所以想要更新操作系统内核中的 TCP 协议也是非常困难的。

QUIC 协议

HTTP/3 选择了一个折中的方法 — UDP 协议。 基于 UDP 实现了类似于 TCP 的多路数据流、传输可靠性等功能,我们把这套功能称为 QUIC 协议。

  • 实现了类似 TCP 的流量控制、传输可靠性功能
  • 集成了 TLS 加密功能
  • 实现了 HTTP/2 中的多路复用功能
  • 实现了快速握手功能

二、HTTP 报文

请求报文

一个 HTTP 请求报文由请求行(request line)请求头(header)空行请求数据 4个部分组成。

请求行

包括请求方法字段、URL 字段和 HTTP 协议版本,如:GET /index.html HTTP/1.1

请求头

请求头由关键字 / 值组成,每行一对,关键字和值用英文冒号隔开

请求头部通知服务器有关于客户端请求的信息,典型的请求头有:

  • User-Agent:产生请求的浏览器类型
  • Accept:客户端可识别的内容类型列表
  • Host:请求的主机名,允许多个域名同处于一个 IP 地址,即虚拟主机
  • Content-Type:请求体的 MIME 类型(用于 POST 和 PUT 请求中),如:Content-Type:application/x-www-form-urlencoded
  • 空行

最后一个请求头之后是空行,发送回车符和换行符,通知服务器以下不再有请求头

请求数据

请求数据不在 get 方法中使用,而是 post 方法中使用。post 方法适用于需要客户填写表单的场合,与请求数据相关的最常使用的请求头是 Content-Type 和 Content-Length

响应报文

响应报文由状态行响应头空行响应正文 组成

三、HTTPS

https是加密了的 http 协议,建立在tcp、ssl协议之上,采用混合加密机制,使用非对称加密传输密钥、使用对称加密传输数据。原因是对称加密需要使用私钥加密解密,但是传输密钥的时候可能会被窃取,所以采用非对称加密传输密钥,但是非对称加密效率比较低,步适合传输数据,所以使用对称加密传输数据。为了认证密钥本身的安全,就有了第三方数字证书认真机构。

http(s)协议 - 图43

  1. 服务端将自己的公钥登录至数字证书认证机构,数字证书认证机构用自己的私钥对服务端公钥署数字签名;
  2. 客户端发出 HTTPS 请求,请求服务端建立 SSL / TLS 连接;
  3. 服务端接收到 HTTPS 请求,将申请到的数字证书和服务端公钥一同返回给客户端;
  4. 客户端在接收到服务端公钥后,利用数字证书认证机构提前植入到浏览器的认证公钥,向数字证书认证机构认证公钥证书上的数字签名,确认服务器公钥的真实性;
  5. 认证通过之后,客户端随机生成通信使用的密钥,然后使用服务端公钥对密钥进行加密,返回给服务端;
  6. 服务端收到加密内容后,通过服务端私钥进行非对称解密,得到客户端密钥,至此双方都获得了对称加密的密钥;
  7. 之后,双方使用密钥进行对称加密通信。
    1. 服务端返回证书
    1. 证书ca机构验证
    1. 客户端生成随机数,用公钥加密,服务端用私钥解密获得随机数,然后通过对称加密加盐的方式返回给客户端
  • 4.后面就一直用对称加密通信了

为了确保数据的完整性,这个时候就需要数字签名,数字签名涉及到了两种技术:非对称加密数字摘要。生成数字摘要的算法通过 MD5SHA 这种不可逆算法,将不定长的报文内容提取出定长的数字摘要。

由于 HTTP 这么不安全,所以为了解决上述的问题,HTTPS 应运而生。目前大部分的网站都已经过渡到了 HTTPS。

http(s)协议 - 图44

http(s)协议 - 图45

上面是我偶然发现的,没记住掘金域名,想用掘金举例子,结果用.com搜出来的并不是掘金,而恰巧它还是http协议的,可以看到,现在的浏览器,如果是 HTTP 协议的,就很明目的提示我们此网站是不安全的,因此我们在输入一些表单私人信息的时候就需要注意了。而第二个图则是掘金的,很明显旁边有个小锁头,看起来就很稳妥有安全感~

出上面两种外,其实还有其他状态比如HTTPS域名下引用了部分 HTTP 内容,那么就会是另一种状态,如下图所示:(一来说,如果是公司项目,最好将网络的图片和 js 脚本放到自己的域名下)

http(s)协议 - 图46

HTTPS 本质也是基于 HTTP 协议的,不过通过一些安全手段来解决上面 HTTP 存在的问题。它的通用接口部分使用 SSL(Secure Socket Layer) 和 TLS(Transport Layer Security) 协议代替。以前来说,HTTP 协议是应用层协议,直接和下层 TCP 进行通信,而增加了 SSL 协议之后,就变成了 HTTP 先跟 SSL 通信再由 SSL 跟 TCP 通信,也就是说HTTPS是披着 SSL 协议外壳的 HTTP 协议。

http(s)协议 - 图47

SSL 是独立于 HTTP 的协议,所以不光是 HTTP 协议,其他运行在应用层的 SMTP 和 Telnet 等协议均可配合 SSP 协议使用。可以说 SSL 是当今世界上应用最为广泛的网络安全技术。

简单来说 HTTPS = HTTP + 加密 + 证书 + 完整性保护

加密

HTTPS 采用混合加密机制,也就是对称加密与非对称加密混用来实现加密机制

数据传输阶段(对称密钥加密)

对称密钥加密又称为共享密钥加密(Common key crypto system),是在加密和解密阶段使用同一个密钥的方式。密钥在传递途中可能会被黑客窃取,那他就可以在之后随意解密收发的数据,通信过程也就没有机密性可言了。

http(s)协议 - 图48

因此,加密的重中之重就是 如何安全地发送密钥并不泄漏

证书交换验证阶段(非对称加密)

公开密钥加密(Public-key cryptography)解决了上述的发送密钥问题。它采用一对非对称的密钥,一把公钥一把私钥。加密过程就是,发送加密报文的一方是用对方的公开密钥进行加密,接收方式用自己本地的私钥进行解密,也就是说发送方并不需要附带着发送用来解密的密钥,这种方式就不需要考虑密钥在传输过程中被攻击这获取到。

私钥和公钥是一对多的关系,公钥可以随意转发,只要采用公钥加密的报文,都只能使用对应私钥进行解密。

混合验证机制

HTTPS 采用的是混合加密。

原因是,非对称加密相比对称加密更加复杂,效率更低,在前端业务中一般都是存在大量的 HTTP 请求,所以非对称加密的低效是无法被接受的。此外,非对称加密的场景只在服务端保存私钥,也就是说一对公私钥只能单向传输数据,因此可以用来确认通信安全以及服务端返回证书。确认安全之后,传输数据采用的就是速度更快的对称加密。

在通信刚开始的时候使用非对称算法,比如 RSA、ECDHE,首先解决密钥交换的问题。

对方拿到密文后用私钥解密,取出会话密钥。这样,双方就实现了对称密钥的安全交换,后续就不再使用非对称加密,全都使用对称加密。

http(s)协议 - 图49

证书

上面的过程也存在一个问题,安全的本质是使用密钥进行加密。但是如果密钥本身就有问题,那么安全也就无从谈起,因此这个密钥必须是通信双方认可的。这个工作不能交给客户端做,也不能服务端做,一半交给第三方权威机构 — 数字证书认证机构(CA,Certificate Authority)。

http(s)协议 - 图50

认证机关的公开密钥必须安全地转交给客户端,使用通信方式是,如何安全转交是一件很困难的事,因此多数浏览器发布版本时,都会是现在内部置入常用认证机关的公钥。

数据完整性

确保数据完整性,也就意味着数据安全没有被第三方篡改,这时候就需要通过 数字签名

数字签名

数字签名是一段由发送者生成的特殊加密校验码,用于传输过程中确认报文的完整性。数字签名涉及到了两种技术:非对称加密数字摘要。生成数字摘要的算法通过 MD5SHA 这种不可逆算法,将不定长的报文内容提取出定长的数字摘要。

数字签名的整个签名和校验过程分为五步:

  1. 发送方用摘要算法对报文提取生成数字摘要
  2. 使用私钥对摘要进行加密,加密后的摘要作为数字签名附加在报文上一起发送给接收方
  3. 接收方收到报文后,使用相同的摘要算法提取出摘要
  4. 在使用公钥对报文的数字签名进行解密
  5. 如果解密后的数字签名与提取出的摘要相同,那么说明报文没有被篡改,数据是完整的

多说一句,对于本地存储,无论是服务端的私钥还是客户端的随机数,都不是 HTTPS 通信过程的安全考虑,HTTPS 只保证在网络传输过程的数据安全性,本地的内容安全不被窃取依靠的是防火墙,杀毒软件等等。

http(s)协议 - 图51

完整流程

证书验证阶段

  1. 客户端发起 HTTPS 请求
  2. 服务端返回 HTTPS 证书
  3. 客户端验证证书是否合法,不合法则提示警告

数据传输阶段

  1. 当证书验证合法后,在本地生成随机密码串
  2. 通过公钥加密随机密码串,并把加密后的随机密码串传输到服务端
  3. 服务端通过私钥对随机密码串进行解密
  4. 服务端通过客户端传入的随机密码串构建对称加密算法,对返回的结果内容进行加密后传输

HTTPS 并不全是优点

速度慢

HTTPS 速度会相比 HTTP 慢 2~100 倍

HTTPS 慢其实是慢在 SSL 协议通信商上,因为 SSL 协议要进行加密解密处理,会占用 CPU 和网络资源,总体会慢一些

http(s)协议 - 图52

CA 证书一般不免费

申请 CA 证书是需要花钱的,当然有很多手段可以申请免费的 HTTPS 证书,但是大部分权威机构还是需要收费的。所以对于大部分小型开发者来说,使用 HTTP 协议更划算。

相关题目

为什么 HTTP 不安全

  1. 报文是明文的,没有加密
  2. 无法验证报文的完整性,传输过程中可能会被篡改
  3. 不验证通信双方的身份,可能会被伪装

为什么 HTTPS 是安全的

HTTPS = HTTP + 加密 + 证书 + 完整性保护。

相关安全操作是通过 SSL 协议来进行实现的。

非对称加密为什么慢,非对称加密除了慢外还有什么缺点?

非对称加密基于大数运算,比如大素数或者椭圆曲线,是复杂的数学难题,所以消耗计算量,运算速度慢。

除了慢,可能还有一个缺点就是需要更多的位数,相同强度的对称密钥要比非对称密钥短。

对称密钥一般都128位、256位,而rsa一般要2048位,不过椭圆曲线的会短一点。

HTTPS 绝对安全吗

并不是,HTTPS 也会被抓包,只不过内容被加密过。不过用户可以主动对证书进行授权,如果用户授权通过,那么代理软件是可以对传输内容进行解密的。

非对称加密为什么慢,非对称加密除了慢外还有什么缺点?

非对称加密基于大数运算,比如大素数或者椭圆曲线,是复杂的数学难题,所以消耗计算量,运算速度慢。

除了慢,可能还有一个缺点就是需要更多的位数,相同强度的对称密钥要比非对称密钥短。

对称密钥一般都128位、256位,而rsa一般要2048位,不过椭圆曲线的会短一点。

HTTPS 握手过程

  1. 服务端将自己的公钥登录至数字证书认证机构,数字证书认证机构用自己的私钥对服务端公钥署数字签名;
  2. 客户端发出 HTTPS 请求,请求服务端建立 SSL / TLS 连接;
  3. 服务端接收到 HTTPS 请求,将申请到的数字证书和服务端公钥一同返回给客户端;
  4. 客户端在接收到服务端公钥后,数字证书认证机构利用提前植入到浏览器的认证公钥,向数字证书认证机构认证公钥证书上的数字签名,确认服务器公钥的真实性;
  5. 认证通过之后,客户端随机生成通信使用的密钥,然后使用服务端公钥对密钥进行加密,返回给服务端;
  6. 服务端收到加密内容后,通过服务端私钥进行非对称解密,得到客户端密钥,至此双方都获得了对称加密的密钥;
  7. 之后,双方使用密钥进行对称加密通信。

四、HTTP三次握手四次挥手

三次握手

http(s)协议 - 图53

第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

四次挥手

  1. http(s)协议 - 图54
    1)客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
    2)服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
    3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
    4)服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
    5)客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
    6)服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

为什么建立连接是三次握手,关闭连接是四次挥手

建立连接

为了实现可靠数据传输,TCP 协议的通信双方都必须维护一个序列号,以标志发送出去的数据包中,哪些是已经被对方收到的。三次握手的过程即是通信双方相互告知序列号起始值,并确认对方已经收到了序列号起始值的必经步骤。

如果只是两次握手,至多只有连接发起方的起始序列号能被确认,另一方选择的序列号则得不到确认,防止已失效的连接请求报文发送到服务端引发错误。

关闭连接

关闭连接时,服务方收到对方的关闭请求时,仅仅表示对方不再发送数据了,但是仍然能够接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后关闭。

所以之所以是四次挥手而不是三次挥手,则是需要确保数据能够完全完成传输。

五、TCP 和 UDP 区别

UDP 在传送数据之前不需要先建立连接,远地主机在收到 UDP 报文后,不需要给出任何确认。虽然 UDP 不提供可靠交付,但在某些情况下 UDP 确是一种最有效的工作方式(一般用于即时通信),比如:QQ 语音、 QQ 视频 、直播等等。

http(s)协议 - 图55

六、前后端鉴权

前后端鉴权是一个很大的话题,不同组织的鉴权方式各不相同,甚至对同一协议的业务实现也可能相去甚远。

文章主要包含三个部分:

  • 区分认证和授权
  • 常见的认证及授权方式
  • 企业应用中常见的单点登录(SSO)方案

认证与授权

首先我们来简单看一下认证和授权的区别,理清楚二者之间的关系。

认证(Authentication)

认证涉及一方应用和一方客户,用于描述客户在该应用下的身份。认证可以简单理解为登录,以此确认你是一个合法的用户。比如掘金就必须登录才能点赞评论收藏。

授权(Authorisation)

授权涉及两方应用和一方客户,用于描述第三方应用有哪些操作权限。

代入场景区分认证和授权

只认证不授权

上面我们使用掘金账号登录掘金就是只认证不授权,此时掘金只知道你是哪个用户,但是不涉及到授权操作。

既认证又授权

同样是登录掘金,我们可以不使用掘金账号进行登录,而是选择第三方应用登陆,比如使用微信登录。这个过程就是掘金向微信申请授权,获取微信用户的信息,用以注册掘金的账户。这个过程及完成了认证(注册成为合法用户)同时又进行了授权(掘金向微信申请授权获取个人信息)。

不认证只授权

以某外卖小程序为例,在你第一次进入外卖小程序时,小程序会弹出浮层请求获取个人信息,此时相当于上面提到的既认证又授权。你同意之后就相当于使用微信登录,但是此时小程序获取到的信息并不包括你的手机号,当你要下单点击提交时,小程序再次发起请求,要获取你绑定的手机号,此时发生的动作就是不认证只授权。

有哪些常见的认证和授权方式

一旦涉及认证,就必须要考虑一个问题就是状态管理。所谓的状态管理就是我们在一个网站进行登录之后一段时间内,再次访问该网站不需要重新登录,所以开发者必须要考虑怎样保持用户的登录状态已决定何时生效何时失效。而这个过程需要前后端协同开发。

Session-Cookie 认证

流程

  1. 用户先使用用户名和密码登录
  2. 服务端拿到登录信息之后,将用户信息保存在 session 中,并把 sessionID 写到前端 cookie 里面
  3. 之后的每一次请求,前端都会带上 cookie,后端只需要通过获取 cookie 中的 sessionID,判断 sessionID 是否过期

主要问题

  • cookie 安全性问题。攻击者可以通过 xss 获取 cookie 中的 sessionID,使用 httpOnly 在一定程度上可以提高安全性。
  • 过多的 session 会消耗较大的服务器资源
  • 分布式下 session 共享问题

Token 认证

与上面的 Session-Cookie 机制不同的是,基于 token 的用户认证是一种服务端无状态的认证方式,服务端可以不用存放 token 数据,但是服务端需要认证 token 的合法性和有效性。使用 token 进行认证的方式这里主要介绍两种:SAML 和 JWT。

SAML(Security Assertion Markup Language)

http(s)协议 - 图56

流程
  1. 未登录的用户通过浏览器访问资源网站
  2. 网站发现用户未登录,将页面重定向到登录页面
  3. 登录页面提供表单给用户进行登录
  4. 用户登录成功后,登录页面生成并发送 SAML token(一个很大的 XML 对象)个资源网站
  5. 网站对 token 进行验证,解析获取用户信息,允许用户访问相关资源

网站是如何验证 token 的合法性的

登录页面发送给资源网站的 token 使用了登录页面的私钥进行加密,资源网站在通过公钥进行解密。

网站是如何判断 token 是否过期

SAML token 中携带了 token 的过期时间。

token 是托管在资源网站还是前端

都可以。如果放在前端,需要前端通过单独的请求获取 token 并保存在本地。如果是托管在网站,则需要引入 session,又变回了 session-cookie 模式。

JWT(JSON Web Token)

JSON Web Token 入门教程

简而言之,JWT 就是一种在用户登录后生成 token,并把 token 放在前端,后端不需要维护用户的状态信息,但是可以验证 token 的有效性。

JWT 用于签名和验证签名的 secret 对于所有人来说都是一样的吗?

secret 使用服务器的私钥,也就是所有用于都是一样的。

SAML 对比 JWT

http(s)协议 - 图57

http(s)协议 - 图58

可以看出,JWT 的体积比 SAML 要小非常多。

OAuth 授权

理解 OAuth 2.0

SSO 和 CAS

单点登录是一个企业应用绕不开的问题,用户在一定时间内登录公司内的其中一个服务,就可以无需再次登录去访问其他所有服务。

在单点登录领域,CAS(Central Authentication Service,中央认证服务)是一个常用解决方案。

CAS 具体实现依赖很多种协议,比如 OpenID、OAuth、SAML 等。

重要概念

  • CAS Server:用于认证的中央服务器
  • CAS Clients:保护 CAS 应用,一旦有未认证的用户访问,重定向到 CAS Server 进行认证
  • TGT & TGC:用户认证之后,CAS Server 会生成一个包含用户信息的 TGT(Ticket Granting Ticket)并向浏览器写一 cookie(TGC,Ticket Granting Cookie)
  • ST:在 URL 上作为参数传输的 ticket,受保护应用可以凭借这个 ticket 去 CAS Server 确认用户是否合法

核心流程

http(s)协议 - 图59

http(s)协议 - 图60

  1. 用户通过浏览器访问 app1 首页
  2. app1 的 CAS Client 通过检测 session 的方式判断用户未进行认证,将用户重定向(第一次重定向)到 CAS Server,url 上携带的参数包含了 app1 的访问地址
  3. CAS Server 察觉到用户浏览器没有 TGC,提供表单给用户登录。用户登录成功后,CAS Server 生成高喊用户信息的 TGT,并将 TGC 写到用户的浏览器 cookie 中

    1. TGC 和 TGT 向关联,是用户浏览器直接向 CAS Server 获取 ST 的票据,如果 TGC 有效,用户就不需要完成表单信息填写步骤直接完成登录
    2. TGC 的过期策略是这样设置的,如果用户一直没有页面操作和后台接口请求,那么默认 2 小时过期。如果一直有操作,默认 8 小时过期。
      1. # most-recently-used expiration policy
      2. cas.ticket.tgt.timeout.maxTimeToLiveInSeconds=7200
      3. # hard timeout policy
      4. cas.ticket.tgt.timeout.hard.maxTimeToLiveInSeconds=28000
  4. CAS Server 把浏览器重定向回 app1 首页(第二次重定向),此时 URL 上携带了 ST

  5. app1 再次接收到用户浏览器的访问,获取到 URL 上面的 ST,然后用 ST 向 CAS Server 询问用户是否已经完成认证。CAS Server 给出肯定的响应后,app1 拿掉 URL 上面的 ST 再次重定向回 app1 的首页(第三次重定向)
    1. app1(CAS Client)凭借 ST 去向 CAS Server 确认当前用户登录状态的同时,获取了用户信息
    2. CAS Client 将这些信息保存在 session 中,并把 sessionID 返回给前端
  6. 用户浏览器去访问同一认证体系下 app2 首页
  7. 同第 2 步,到了 CAS Server 后,CAS Server 检测到了浏览器的 TGC,找到了对应的 TGT,验证是合法的,然后同第 4 步、第 5 步

几个问题

如何避免 sessionID 冲突

使用各自子服务的特有名称作为 sessionID 的前缀

假设 a 和 b 都进行过单线登录认证,有没有可能 a 已过期,但 b 还没过期

不会。在实际业务中,CAS Client 会定期向 CAS Server 进行通信,如果用户一致操作,那么 CAS Server 就会不停 TGC 的过期时间,最终对于 a 和 b 来说,TGC 的过期时间一定是相同的。

七、webSocket通信

https://www.cnblogs.com/goloving/p/10686826.html

最近在项目中用到了websocket通信,之前就了解过它的功能,第一次真正使用这个大名鼎鼎的通信协议,激动的心 ,颤抖的手,,,快来会会这个老朋友。

先来复习一波。

“WebSocket”是一种基于 TCP 的全双工网络通信协议。

1.为什么要有 WebSocket

HTTP/2 针对的是“队头阻塞”,传输效率低下的问题,而 WebSocket 针对的是“请求 - 应答”通信模式。

“请求 - 应答”是一种“半双工”的通信模式,它是一种“被动”通信模式,服务器只能“被动”响应客户端的请求,无法主动向客户端发送数据。

虽然后来的 HTTP/2、HTTP/3 新增了 Stream、Server Push 等特性,但“请求 - 应答”依然是主要的工作方式。这就导致 HTTP 难以应用在动态页面、即时消息、网络游戏等要求“实时通信”的领域。

2.WebSocket 的特点

2.1 全双工

WebSocket 是一个真正“全双工”的通信协议,客户端和服务器都可以随时向对方发送数据。

一旦后台有新的数据,就可以立即“推送”给客户端,不需要客户端轮询,“实时通信”的效率也就提高了。

2.2 二进制帧结构

WebSocket 虽然有“帧”,但却没有像 HTTP/2 那样定义“流”,也就不存在“多路复用”“优先级”等复杂的特性,而它自身就是“全双工”的,也就不需要“服务器推送”。

2.3WebSocket 的握手

WebSocket 的握手是一个标准的 HTTP GET 请求,但要带上两个协议升级的专用头字段和两个认证用头字段:

  • “Connection: Upgrade”,表示要求协议“升级”;
  • “Upgrade: websocket”,表示要“升级”成 WebSocket 协议。
  • Sec-WebSocket-Key:一个 Base64 编码的 16 字节随机数,作为简单的认证密钥;
  • Sec-WebSocket-Version:协议的版本号,当前必须是 13。

服务器收到 HTTP 请求报文,看到上面的四个字段,就知道这不是一个普通的 GET 请求,而是 WebSocket 的升级请求,于是就不走普通的 HTTP 处理流程,而是构造一个特殊的“101 Switching Protocols”响应报文,通知客户端,接下来就不用 HTTP 了,全改用 WebSocket 协议通信。

3.WebSocket 与 HTTP/2 的异同点

同:

  • 都可以从 HTTP/1 升级,都采用二进制帧结构。

异:

  • HTTP/2是请求与响应的模式,在WebSocket是“全双工”,没有请求响应的概念,服务器也可以主动向客户端发起请求,收发的都是数据帧。
  • websocket里面有帧的概念,却没有http2.0里的虚拟流的概念,也不存在优先级、多路复用。
  • websocket的出现本质上还是为了解决http的半双工的问题,变成全双工,服务器和客户端可以随意通行的问题。

工作场景遇到过用户订阅股票的股价,股价波动时实时推送给海量订阅的用户,面试场景被问到两次,一 千万粉丝的明星发布动态如何推送给粉丝 二 海量用户的主播直播如何推送弹幕 当时回答消息队列,其实web socket才是比较好的方案。 WebSocket适合实时通信交互的场景,和消息队列其实是两个领域,不冲突,可以互相结合使用。

4.实际应用

基于webSocket通信的库主要有 socket.ioSockJS,这里使用的是 SockJS。

引入模块

需要在项目中引入sockjs-clientstomjs这两个模块。

  1. import SockJS from "sockjs-client";
  2. import Stomp from "stompjs";
  3. import store from "./store";
  4. import router from "@/router";
  5. import { Notification } from "element-ui";

请求参数配置
  1. const sockJS = new SockJS(`//${process.env.VUE_APP_WS_URL}/ws`); // 请求地址
  2. stomp = Stomp.over(sockJS); // 用来定义消息语义.
  3. stomp.heartbeat.outgoing = 10000; // 客户端向服务端发送心跳包
  4. stomp.heartbeat.incoming = 0; //服务端 不向客户端发送心跳包

这个心跳包就是用来检测连接情况,之所以采用客户端向服务器发送而不是服务端向客户端发送,主要是考虑到服务器的压力,当用户很多的时候,比如淘宝等,那么就需要服务器维护一个很长的tcp连接,向每个用户都发送心跳包,这样服务器就压力很大。所以采用客户端发送心跳包。

向服务器发起websocket连接
  1. stomp.connect(
  2. {},
  3. () => {
  4. // 订阅事件类型 发送关于的什么的通知
  5. stomp.subscribe(`/token/01_${store.state.user.name}/event`, (res) => {
  6. // 服务器推送的数据
  7. const content = JSON.parse(res.body);
  8. const { data } = content;
  9. const { result } = data;
  10. const { title, msg, id, type } = data;
  11. // 客户端要提示的消息配置
  12. const options = {
  13. title,
  14. message: msg,
  15. onClick: () => {
  16. // 跳转 并刷新详情页
  17. // 金融产品状态变更
  18. if (
  19. type === "product" &&
  20. router.currentRoute.path !== "/my-financial-products/detail"
  21. ) {
  22. router.push({
  23. path: "/my-financial-products/detail",
  24. query: { id },
  25. });
  26. }
  27. // 融资申请管理
  28. if (
  29. type === "order" &&
  30. router.currentRoute.path !==
  31. "/financing-application-management/application-detail"
  32. ) {
  33. router.push({
  34. path: "/financing-application-management/application-detail",
  35. query: { id },
  36. });
  37. }
  38. },
  39. };
  40. // 存到store ,如果当前在详情页,就通过watch监听变化,触发详情页刷新
  41. store.commit(
  42. "app/SET_ORDER_DETAIL_TOGGLE",
  43. !store.state.app.orderStatus
  44. );
  45. if (result === "pass") {
  46. options.type = "success";
  47. Notification(options);
  48. } else if (result === "fail") {
  49. options.type = "error";
  50. Notification(options);
  51. } else if (result === "default") {
  52. options.type = "info";
  53. Notification(options);
  54. }
  55. });
  56. },
  57. // 若出现超时未连接成功的情况就会重新连接一次
  58. connect
  59. );

通过subscribe订阅要推送的消息类型,根据推送回来的数据在页面显示对应的提示。

完整代码:

  1. import SockJS from "sockjs-client";
  2. import Stomp from "stompjs";
  3. import store from "./store";
  4. import router from "@/router";
  5. import { Notification } from "element-ui";
  6. let stomp;
  7. export default function connect() {
  8. if (!stomp || !stomp.connected) {
  9. const sockJS = new SockJS(`//${process.env.VUE_APP_WS_URL}/ws`); // 请求地址
  10. stomp = Stomp.over(sockJS); // 格式化消息
  11. stomp.heartbeat.outgoing = 10000; // 客户端向服务端发送心跳包
  12. stomp.heartbeat.incoming = 0; //服务端 不向客户端发送心跳包
  13. // 向服务器发起websocket连接
  14. stomp.connect(
  15. {},
  16. () => {
  17. // 订阅事件类型 发送关于的什么的通知
  18. stomp.subscribe(`/token/01_${store.state.user.name}/event`, (res) => {
  19. // 服务器推送的数据
  20. const content = JSON.parse(res.body);
  21. const { data } = content;
  22. const { result } = data;
  23. const { title, msg, id, type } = data;
  24. // 客户端要提示的消息配置
  25. const options = {
  26. title,
  27. message: msg,
  28. onClick: () => {
  29. // 跳转 并刷新详情页
  30. // 金融产品状态变更
  31. if (
  32. type === "product" &&
  33. router.currentRoute.path !== "/my-financial-products/detail"
  34. ) {
  35. router.push({
  36. path: "/my-financial-products/detail",
  37. query: { id },
  38. });
  39. }
  40. // 融资申请管理
  41. if (
  42. type === "order" &&
  43. router.currentRoute.path !==
  44. "/financing-application-management/application-detail"
  45. ) {
  46. router.push({
  47. path: "/financing-application-management/application-detail",
  48. query: { id },
  49. });
  50. }
  51. },
  52. };
  53. // 存到store ,如果当前在详情页,就通过watch监听变化,触发详情页刷新
  54. store.commit(
  55. "app/SET_ORDER_DETAIL_TOGGLE",
  56. !store.state.app.orderStatus
  57. );
  58. // store.commit(`app/SET_HINT_TOGGLE`, !store.state.app.hintToggle);
  59. // if (
  60. // router.currentRoute.path !== "/entside/personalcenter/systeminfo"
  61. // ) {
  62. // store.commit("app/SET_HINT_DISPLAY", true);
  63. // }
  64. if (result === "pass") {
  65. options.type = "success";
  66. Notification(options);
  67. } else if (result === "fail") {
  68. options.type = "error";
  69. Notification(options);
  70. } else if (result === "default") {
  71. options.type = "info";
  72. Notification(options);
  73. }
  74. });
  75. },
  76. // 若出现超时未连接成功的情况就会重新连接一次
  77. connect
  78. );
  79. }
  80. return stomp;
  81. }

关于为什么要用SockJS与stomp

1、SockJS

SockJS是一个浏览器的JavaScript库,它提供了一个类似于网络的对象,SockJS提供了一个连贯的、跨浏览器的JavaScriptAPI,它在浏览器和Web服务器之间创建了一个低延迟、全双工、跨域通信通道。你可能会问,我为什么不直接用原生的WebSocket而要使用SockJS呢?这得益于SockJS的一大特性,一些浏览器中缺少对WebSocket的支持,因此回退选项是必要的,而Spring框架提供了基于SockJS协议的透明的回退选项。SockJS提供了浏览器兼容性,优先使用原生的WebSocket,如果某个浏览器不支持WebSocket,SockJS会自动降级为轮询。

2、stomjs

直接使用WebSocket ,返回的是将字节流转化为文本/二进制消息,不是语义化的消息,因此可以在 WebSocket 之上使用STOMP协议,来为浏览器 和 server间的通信增加适当的消息语义。

STOMP(Simple Text-Orientated Messaging Protocol) 面向消息的简单文本协议,WebSocket是一个消息架构,不强制使用任何特定的消息协议,它依赖于应用层解释消息的含义。与HTTP不 同,WebSocket是处在TCP上非常薄的一层,会将字节流转化为文本/二进制消息,因此,对于实际应用来说,WebSocket的通信形式层级过低,因此可以在 WebSocket 之上使用STOMP协议,来为浏览器 和 server间的通信增加适当的消息语义。

STOMP与WebSocket 的关系:

HTTP协议解决了web浏览器发起请求以及web服务器响应请求的细节,假设HTTP协议不存在,只能使用TCP套接字来编写web应用,你可能认为这是一件疯狂的事情

直接使用WebSocket(SockJS)就很类似于使用TCP套接字来编写web应用,因为没有高层协议,就需要我们定义应用间发送消息的语义,还需要确保连接的两端都能遵循这些语义;

同HTTP在TCP套接字上添加请求-响应模型层一样,STOMP在WebSocket之上提供了一个基于帧的线路格式层,用来定义消息语义.

八、cookie

2 月份发布的 Chrome 80 版本中默认屏蔽了第三方的 Cookie,这导致了线上非常多的问题,着实推动了大家对 Cookie 的理解,所以很有可能会有相关的面试题,即便不是面试题,当问到 HTTP 相关内容时,不妨也扯到这件事情上,一能表明你对前端时事的跟进,二能借此引申到前端安全方面的内容,为你的面试加分。

本文就给大家介绍一下浏览器的 Cookie 以及这个“火热”的 SameSite 属性。

HTTP

一般我们都会说「HTTP 是一个无状态协议」,不过要注意这里的 HTTP 其实指的是 HTTP/1.x,而所谓无状态协议,简单的理解便是即使同一个客户端连续两次发送请求给服务器,服务器也识别不出这是同一个客户端发送的请求,这导致的问题就是比如你加了一个商品到购物车中,但因为识别不出是同一个客户端,你刷新页面就消失了。

Cookie

为了解决 HTTP 无状态导致的问题,后面推出了 Cookie。

不过这样说可能会让你产生一些误解。首先无状态并不是不好,有优点,但也会导致一些问题。而 Cookie 的存在也不是为了解决通讯无状态的问题,只是为了解决客户端与服务端会话状态的问题,这个状态是指后端服务的状态而非通讯协议的状态。

Cookie 介绍

我们看一下 Cookie,引用维基百科:

Cookie,类型为「小型文本文件」,指某些网站为了辨别用户身份而储存在用户本地终端上的数据。

作为一段一般不超过 4KB 的小型文本数据,它由一个名称(Name)、一个值(Value)和其他几个用于介绍控制 Cookie 有效期、安全性、使用范围的可选属性组成,这涉及的属性我们会在后面介绍。

Cookie 的查看

我们可以在浏览器开发者工具中查看到当前页面的 Cookie,尽管我们是在浏览器中看到了 Cookie,这并不意味着 Cookie 文件只是存放在浏览器里。实际上,Cookie 相关的内容还可以存在本地文件中,就比如说 Max 下的 Chrome,存放目录就是 ~/Library/Application Support/Google/Chrome/Default ,里面会有一个名为 Cookies 的数据库文件,你可以使用 sqlite 软件打开它。

存放在本地的好处就在于即使你关闭了浏览器,Cookie 依然可以生效。

Cookie 的设置

简单来说:

  1. 客户端发送 HTTP 请求
  2. 服务器收到 HTTP 请求,在响应头里面添加一个 Set-Cookie 字段
  3. 浏览器受到相应后保存下 Cookie
  4. 之后对该服务器每一次请求中都通过 Cookie 字段将 Cookie 信息发送给服务器

Cookie 的属性

Name/Value

用 JS 操作 Cookie 的时候注意对 Value 进行编码处理。

Expires

用于设置 Cookie 的过期时间。

当 Expires 缺省的时候,表示是会话性 Cookie。当 Cookie 为会话性时,值会保存在浏览器内存中,用户关闭浏览器时会失效。需要注意的是,部分浏览器提供了会话恢复功能,这种情况下即便关闭了浏览器,会话期 Cookie 也会被保留下来,就好像浏览器从来没有关闭一样。

与会话性 Cookie 相对的是持久性 Cookie,持久性 Cookie 会保存在用户的硬盘中,直至过期或者清除 Cookie。

这里值得注意的是,设定的日期和时间只和客户端相关,而不是服务端。

Max-Age

用于设置在 Cookie 失效之前需要经过的秒数。

Max-Age 可以为正数、负数、甚至是 0.

如果为正数,浏览器会将其持久化,即写到对应的 Cookie 文件中。

如果属性为负数,表示该 Cookie 只是一个会话性 Cookie。

当为 0 时,会立即删除这个 Cookie。

如果 Expires 和 Max-Age 同时存在时,Max-Age 优先级更高。

Domain

Domain 制定了 Cookie 可以送达的主机名。假如没有指定,那么默认值为当前文档访问地址中的主机部分(但是不包含子域名)。

像淘宝首页设置的 Domain 就是 .taobao.com ,这样不论是 a.taobao.com 还是 b.taobao.com 都可以使用 Cookie。

这里需要注意的是,不能跨域设置 Cookie,这样设置是无效的。

Path

Path 指定了一个 URL 路径,这个路径必须出现在要请求的资源的路径中才可以发送 Cookie 首部。比如设置了 Path=/docs 下的资源会带 Cookie 首部, /test 则不会携带 Cookie 首部。

Domain 和 Path 标识共同定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。

Secure 属性

标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。使用 HTTPS 安全协议可以保护 Cookie 在浏览器和服务器之间传输过程不被窃取和篡改。

HTTPOnly

设置 HTTPOnly 属性可以防止客户端脚本通过 document.cookie 等方式访问 Cookie,有助于避免 XSS 攻击。

SameSite

Cookie 的**SameSite**属性用来限制第三方 Cookie,从而减少安全风险。

它可以设置三个值。

  • Strict
  • Lax
  • None

2.1 Strict

Strict最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。

  1. Set-Cookie: CookieName=CookieValue; SameSite=Strict;

这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态。

2.2 Lax

Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。

  1. Set-Cookie: CookieName=CookieValue; SameSite=Lax;

导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。详见下表。

请求类型 实例 以前 Strict Lax None
链接 `` 发送 cookie 不发送 发送 cookie 发送 cookie
预加载 `` 发送 cookie 不发送 发送 cookie 发送 cookie
get 表单 `` 发送 cookie 不发送 发送 cookie 发送 cookie
post 表单 `` 发送 cookie 不发送 不发送 发送 cookie
iframe `` 发送 cookie 不发送 不发送 发送 cookie
AJAX $.get("...") 发送 cookie 不发送 不发送 发送 cookie
Image `` 发送 cookie 不发送 不发送 发送 cookie

从表格中可以看到,对大部分 web 应用而言,Post 表单、iframe、ajax、image 这四种情况从跨站会发送第三方 Cookie 变成了不发送。

2.3 None

Chrome 计划将Lax变为默认设置。这时,网站可以选择显式关闭SameSite属性,将其设为None。不过,前提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效。

下面的设置无效。

  1. Set-Cookie: widget_session=abc123; SameSite=None

下面的设置有效。

Cookie 的作用

  1. 会话状态管理(如用户登录状态、购物车、游戏分数或其他需要记录的信息);
  2. 个性化设置(如用户自定义设置、主题等);
  3. 浏览器行为跟踪(如跟踪分析用户行为等)。

九、TCP 和 UDP 区别

TCP 提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接。TCP 不提供广播或多播服务。由于 TCP 要提供可靠的,面向连接的运输服务(TCP 的可靠体现在 TCP 在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源),这难以避免增加了许多开销,如确认,流量控制,计时器以及连接管理等。这不仅使协议数据单元的首部增大很多,还要占用许多处理机资源。

UDP 在传送数据之前不需要先建立连接,远地主机在收到 UDP 报文后,不需要给出任何确认。虽然 UDP 不提供可靠交付,但在某些情况下 UDP 确是一种最有效的工作方式(一般用于即时通信),比如:QQ 语音、 QQ 视频 、直播等等。

http(s)协议 - 图61