跳转至

05 http

概述

HTTP(Hyper Text Transfer Protocol),超文本传输协议。是互联网应用最广泛的协议之一。设计HTTP最初的目的是为了发送和接收HTML页面,由URI来标识具体的资源位置。后面HTTP协议就不只是用来传输HTML页面了。

URI和URL的区别,能在全网定位一个资源的URI就是要给URL。

比如: https://liuyangjun.blog.csdn.net/?type=blog这就是一段URL,它能够在全网定位到这个资源。 /login/loginIndex.html 这就是一段URI。

版本变迁

1991年,HTTP/0.9:

  • 支持GET请求方式获取文本数据(比如HTML),且不支持请求头,响应头,无法向服务器传递太多信息。

1996年,HTTP/1.0:

  • 支持POST、HEAD等请求方法,支持请求头、响应头等,支持更多种数据类型(不再局限于文本数据)浏览器的每次请求都需要与服务器建立一个TCP连接,请求处理完成后立即断开TCP连接。比如当前当前HTML在被浏览器解析是,发现由一个图片资源,就是再次建立TCP连接,然后关闭TCP连接。

1997年,HTTP/1.1

  • 支持PUT,DELETE请求,采用持久连接,多个HTTP请求,可以共用一个TCP连接。
  • 这版协议使用最广泛。

2015,HTTP/2.0

2018,HTTP/3.0

HTTP报文格式

HTTP的格式要求非常严格,回车换行的使用都是有明确要求的。漏掉一个回车或者换行,服务器就不认。通常定义HTTP协议最标准的语言就是ABNF,ABNF对于HTTP协议的描述是最准确的。ABNF核心规则在HTTP.pdf里(记得看一看)

请求方法

在RFC 7231中描述了8种请求方法。

GET,POST,HEAD,PUT,DELETE,CONNET,OPTIONS,TRACE

在RFC 5789中又加了一个PATCH方法。

GET:常用于读取操作,请求参数直接拼在URL后面,浏览器对TRL的长度是有限制的。

POST:常用于添加,修改,删除操作,请求参数可以放到请求体中(大小没有限制)

HEAD:该请求能够得到于GET相同的响应,但是没有响应体。场景: 在下载一个文件前,先获取大小,再决定是否需要下载,以此可以节约宽带。

OPTIONS: 用于获取目的资源所支持的通信选项,比如服务器支持的请求方法。

PUT: 用于对已存在的资源进行整体覆盖

PATCH:用于对资源进行部分修改(资源不存在,会创建新的资源)

DELETE: 用于删除指定的资源

TRACE: 请求服务器回显其收到的请求信息,主要用于HTTP请求的测试或诊断

CONNECT: 可以开启一个客户端与所请求资源之间的双向沟通的通道,它可以用来创建隧道(tunnel) 可以用来访问采用了 SSL (HTTPS) 协议的站点

头部字段

按照标准的流程头部字段可以分成四种,请求头字段,响应头字段,实体头字段,通用头字段。但其实按照请求和响应划分,可以只分成两种,请求头字段和响应头字段。

**请求头字段: **

说明 说明 示例
User-Agent 浏览器的身份标识字符串 太长了,打开浏览器按F12查看
Host 服务器的域名,端口号 Host: localhost:8080
Date 发送该消息的日期和时间 太长了,打开浏览器按F12查看
Refere 表示浏览器访问的前一个页面,正是前一个页面的某个链接将浏览器带到当前页面,用于防盗链 Refere: https:www.baidu.com
Content-Type 请求体的类型 只有Post请求才有 太长了,打开浏览器按F12查看
Content-Length 请求体的长度(字节为单位)只有Post请求才有 Content-Length: 348
Accept 能够接收响应内容的类型(Content-Types) Accept: text\plain
Accept-Charset 能够接收的字符文件 F12,看浏览器,但这里有个 q = 值。q代表权重值。如果不指定权重值,默认最大1.0
Accept-Encoding 能够接受的编码方式列表 Accept-Encoding: gzip, deflate, br
Accept-Language 能够接受的响应内容的自然语言列表 Accept-Language: en-US
Range 仅请求某个实体的一部分。字节偏移以0开始。对多线程断点续传有用 Range: bytes=500-999
Origin 发起一个针对跨越资源共享的请求 Origin: https://www.baidu.com
Cookie 之前由服务器通过Set-Cookie发送的Cookie Cookie: $Version=1; Skin=new;
Connection 该浏览器想要优先使用的链接类型 Connection: keep-alive
Cache-Control 用来指定在这次的请求/响应链中的所有缓存机制都必须遵守的指令 Cache-Control: no-cache

**响应头字段: **

说明 说明 示例
Date 发送该消息的日期和时间 太长了,打开浏览器按F12查看
Last-Modified 所请求的对象最后修改日期 F12
Server 服务器的名字 Server: Apache/2.3.1(Unix)
Expires 指定一个时间,超过该时间则认为此响应已经过期 Expires: Fri, 21 Oct 2022 10:52:49 GMT
Content-Type 响应体类型 Content-Type: text/html;charset=UTF-8
Content-Encoding 响应内容使用的编码类型 Content-Encoding: gzip
Content-Length 响应体长度(字节为单位) Content-Length: 285
Content-Disposition 可以让客户端下载文件并建议文件名。比如返回的是一段文本,但是想要让文本变成文件就设置它 Content-Disposition: attachment; filename="superMonkey.txt"
Accept-Ranges 服务器支持哪些种类的部分内容范围
Content-Range 这条部分消息是属于完整消息的那部分 Content-Range: bytes 21010-47021/47022
Access-Control-Allow-Origin 指定哪些网站可参与到跨来源资源共享过程中 Access-Control-Allow-Origin
Location 用来进行重定向,或者创建某个新资源使用 Location: http:www.w3.org
Connection 针对该链接所预期的选项 Connection: close
Set-Cookie 返回一个Cookie让客户端去保存 Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
Cache-Control 向从服务器直到客户端在内的所有缓存机制告知,他们是否可以缓存这个对象。如果有max-age,单位为秒 Cache-Control: max-age=3600

跨域(CORS)

跨域指的是: 浏览器不能执行其他网站的脚本,从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域。跨域是由浏览器的同源策略造成的,是浏览器施加的安全限制。a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,所进行的访问行动都是跨域的。

同源: 协议,域名(IP),端口三者都相同才是同源。

同源策略规定了ajax只能发送同源的url,如果ajax请求非同源就需要设置跨域。

跨域策略: 前后端分离之后,ajax请求肯定会被同源策略限制,此时就需要设置跨域。

想要允许跨域,请求头中都有一个 origin信息,origin后面带着的值就是源头的协议,ip,端口的url , 样例参考请求头的 origin

服务端给回的响应头中也就带着: Access-Control-Allow-Origin信息,值就是Origin中的源头地址,服务器通过这个设置允许谁可以跨域。

场景: 前后端分离项目中,前面项目部署在A服务器上,后台项目部署在B服务器上。当用户先访问A时,此时A会把html返回给客户端浏览器展示,但是有些动态数据是需要通过Ajax访问B才能得到的。此时同源策略就起作用了,因为A和B的IP肯定不一样,浏览器就会拒绝展示B返回的数据。解决方法,在需要跨域时,在请求头中设置 origin信息,然后在B接收到请求时,设置响应头的Access-Control-Allow-Origin信息为请求头中Origin的值,这个值就是允许跨域的源。这样就告诉了浏览器,我这个资源是允许我设置的这些源访问。浏览器收到后就会将放回的Json交给页面。

在没有设置跨域的时候,服务器的json其实已经给浏览器了,但是浏览器发现没有设置跨域,就不会把json交给页面。

java @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置COPS(允许别人能够跨域访问) resp.setHeader("Access-Control-Allow-Origin" , "http://localhost:9896"); }

Cookie和Session

HTTP请求是无状态请求,对于交互场景没有记忆能力。需要使用Cookie和Session让服务器具有记忆能力。

Cookie: 在客户端(浏览器)存储数据,是存储到浏览器所在计算机的硬盘里。在Cookie中有一个domain存储IP和端口,path存储路径。 如果请求时,发现ip,端口,路径都和Cookie中的一样,请求头就会自动带上Cookie。如果没有给Cookie设置有效时间,浏览器关闭,Cookie就销毁

Set-Cookie: 这是响应头的字段,服务器就是通过这个字段告诉客户端需要存储的Cookie,SetCookie带回去的就有SessionID和Path

Session:在服务器存储一些数据,存储到服务器的内存中。每个浏览器在服务器上都有单独的Session对象,Session创建成功之后都有一个SessionID,Cookie中存储的是SessionID。

在请求服务器时,如果请求头中没有Cookie,那么服务器有getSession()时就是重新创建一个Session,然后通过Set-Cookie返回SessionID,要是请求头中有Cookie,那个服务器getSession()就会根据Cookie里面的SessionID获取已经存在的Session对象。

Session在服务器的有效时间时30分钟

状态码

状态码的作用是指示HTTP请求是否已成功完成。状态码的格式固定就是3个数字。

状态码分类:

信息响应: 100 ~ 199

成功响应: 200 ~ 299

重定向: 300 ~ 399

客户端错误: 400 ~ 499

服务器错误: 500 ~ 599

常见状态码:

100 Continue: 客户端想给服务器发送请求体,但是不知道服务端愿不愿意接收自己的请求体,所以会先发一个不带请求体的请求,如果服务端解析客户端的url和请求头中的数据发现请求体是自己能够接收的,服务端就会返回100的状态码,告诉客户端,可以继续发送请求体。

200 OK 请求成功

302 Found: 请求的资源被暂时的移动到有Location头部指定的URL上。

304 Not Modified: 客户端向服务端发送请求时,如果不是第一次请求,服务端也已经响应过一次这个请求,并且服务器本地也没有任何变动,这时服务器就会直接给回客户端304,客户端拿到304,就知道自己需要使用本地缓存。

400 Bad Request: 由于语法无效,服务器无法理解该请求

401 Unauthorized: 由于缺乏目标资源要求的身份验证凭证

403 Forbidden: 服务器端有能力处理该请求,但是拒绝授权访问

404 Not Found: 服务器端无法找到所请求的资源

405 Method Not Allowed: 服务器禁止了使用当前HTTP方法的请求

406 Not Acceptable: 服务器端无法提供与Accept-Charset以及Accept-Language指定的值相匹配的响应

408 Request Timeout: 服务器想要将没有在使用的连接关闭。一些服务器会在空闲连接上发送此信息,即便是在客户端没有发送任何请求的情况下

500 Internal Server Error: 所请求的服务器遇到意外的情况并阻止其执行请求

501 Not Implemented: 请求的方法不被服务器支持,因此无法被处理。服务器必须支持的方法(即不会返回这个状态码的方法)只有 GET 和 HEAD

502 Bad Gateway: 作为网关或代理角色的服务器,从上游服务器(如tomcat)中接收到的响应是无效的。

503 Service Unavailable: 服务器尚未处于可以接受请求的状态。通常造成这种情况的原因是由于服务器停机维护或者已超载

form提交

form属性:

​ action:请求url

​ method: 请求方法,浏览器的表单支持是get和post

​ enctype: POST请求时,请求体的编码方式。application/x-www-form-urlencoded(默认值)。

enctype详解:

作用是控制请求体的编码方式。

使用默认值application/x-www-form-urlencoded就表示,请求体中使用&分割参数,使用=分割健和值,字符用URL编码方式进行编码。

文件上传,文件上传是form表单必须使用 multipart/form-data 的编码方式

使用了multipart/form-data的编码方式,Http请求头中的Content-Type会发生明显变化

代理服务器(Proxy Server)

代理服务器本身不生产内容,处于中间位置,转发上下游的请求和响应。面向下游的客户端,代理服务器是服务器; 面向上游的服务器,代理服务器是客户端。

正向代理

正向代理代理服务器代理的对象是客户端。作用:

  • 隐藏客户端身份,对于服务器而言,它只知道是代理服务器的访问。
  • 绕过防火墙。本来服务器是不允许客户端A访问的,对As设置了防火墙,但是服务器对代理服务器D并没有设置防火墙,那么A就可以访问代理服务器间接的访问服务器。
  • Internet访问控制,路由器连着互联网,交换机连着很多客户端,我们只需要指定其中一台客户端作为代理服务器,通过路由器设置只允许这个代理服务器能够通过路由器上网,那么在这个局域网内的其他客户端就必须通过这个代理服务器才能上网,这样的话代理服务就能够控制谁能上网,这就是Internet访问控制。
  • 数据过滤: 如果代理服务器发现客户端访问的数据是不健康的,就可以过滤掉。

反向代理

反向代理代理的是服务器。作用:

  • 隐藏服务器的身份。
  • 安全防护: 服务器并不直接保留给客户端。
  • 负载均衡: 代理服务器可以根据负载均衡算法选择将客户端的请求分到不同的服务器。代理服务器不产生任何运算,只是转发,所以一般情况下一台就够了。

抓包软件原理

Fiddler,Charles等抓包工具其实就是一个代理服务器,它是正向代理,在客户端启动,那么这个客户端所有的请求都会被这个代理服务器捕捉到。

Whireshark除外,它是直接工作在网卡上,捕获网卡的信息。

VPN和代理的区别:VPN能做的代理都能做,使用Vpn的原因是,VPN是加密的。

CDN

CDN(**C**ontent **D**elivery **N**etwork或**C**ontent **D**istribution **N**etwork): 内容分发网络。

作用: 利用最靠近目标用户的服务器,更快更可靠的将音乐,图片,视频等资源文件(一般是静态资源)传递给用户。

CDN使用前后对比

image-20221130223701250

没有使用CDN: 在不使用CDN的时候,所有的用户获取资源都需要从服务主机上面获取,照成服务主机负载过重。

使用CDN: 服务主机将所有的资源同步到CDN上,CDN是各个地区的服务器连在一起的网络,主要作用是分发内容。然后用户需要获取资源时,找到距离用户物理位置最近的一台服务器获取,这样就减轻服务器负载,获取速度还快。

CDN运营商在全国,乃至全球各个大枢纽城市都建立了机房。部署了大量拥有高存储高宽带的节点,构建了一个跨运营商,跨地域的专用网络。

使用了CDN之后的请求过程: DNS不知道你对应的ip了,DNS会把你的请求给一个 CDN 的 DNS服务器 , 然后这个CDN的DNS服务器就会给你一个CND负载均衡系统的ip,你再去找这个全局负载均衡的系统,此时这个系统就会找出距离你物理地址最近的一台资源服务器的ip,然后你请求距离你最近的这台服务器,返回你要的资源。如果你访问的资源CDN的边缘节点没有,那么就会CDN父层节点,没有再往上层找,直到找到源站。

网络安全

网络通信中面临的4种安全威胁

image-20221130225041857

**截获: **窃听通信内容

**中断: **中断网络通信

**篡改: **篡改通信内容

**伪造: **伪造通信内容

网络攻击的常见手段

ARP欺骗(网络层): 在两台主机第一次通信之前,肯定需要广播协议获取MAC地址和,目的就是将IP和MAC地址弄成不对等的。

ARP欺骗可以造成的效果

  • 可让攻击者获取局域网上的数据包甚至可篡改数据包
  • 可让网络上特定电脑之间无法正常通信(例如网络执法官这样的软件)
  • 让送至特定IP地址的流量被错误送到攻击者所取代的地方

攻击步骤:

假设主机C是攻击者,主机A、B是被攻击者

  • C只要收到过A、B发送的ARP请求,就会拥有A、B的IP、MAC地址,就可以进行欺骗活动
  • C发送一个ARP响应给B,把响应包里的源IP设为A的IP地址,源MAC设为C的MAC地址
  • B收到ARP响应后,更新它的ARP表,把A的MAC地址(IP_A, MAC_A)改为(IP_A, MAC_C)
  • 当B要发送数据包给A时,它根据ARP表来封装数据包的头部,把目标MAC地址设为MAC_C,而非MAC_A
  • 当交换机收到B发送给A的数据包时,根据此包的目标MAC地址(MAC_C)而把数据包转发给C
  • C收到数据包后,可以把它存起来后再发送给A,达到窃听效果。C也可以篡改数据后才发送数据包给A

DoS攻击(拒绝服务攻击),使目标电脑的网络或系统资源耗尽,使服务暂时中断或停止,导致其正常用户无法访问。

DDoS,找其他僵尸机帮你一起攻击服务器,就变成了分布式

DoS两大类,带宽消耗,就是不断发数据包,占满你的带宽,用户就没办法访问。

SYN洪水攻击,原理就是不断的发送tcp三次握手的第一次握手,然后客户端不给第三次握手,服务器就会一直等待,消耗资源。实现: 比如我不断的发送请求,但是我所携带的源地址并不是我自己的源IP,服务器接收到之后就会去返回ACK确认给这个假的源IP,假的源IP这台电脑收到后也懵逼,这不是我发起的,就丢弃,不反回第三次握手,那么服务器就会等待,从而浪费资源。

LAND攻击: 发送请求的的源ip和目标ip是一样的,那服务器收到之后,就会给自己返回ACk确认,自己连接自己,直至崩溃。导致循环应答。部分操作系统已经修复了这种漏洞。

DNS劫持,劫持了DNS,域名解析的时候返回的IP是其他IP,然后就访问成了其他IP,这时可能返回一个假冒的网站,获取你的信息。

HTTP劫持,返回给你浏览器执行的JS代码被篡改了,比如访问有些网站时,在右小角多了个莫名其妙的广告弹窗。

HTTPS

HTTPS(HyperText Transfer Protocol Secure),译为: 超文本传输安全协议。可以理解为,使用 TLS 的HTTP协议,或使用了SSL的HTTP协议,或使用Secure的HTTP协议。HTTPS其实就是对HTTP协议的报文进行加密. HTTPS的默认端口是443,HTTP的默认端口是80。HTTPS相对HTTP变的非常安全,但是HTTPS使用成本比较高,需要支付证书费用,加解密计算,以及降低了访问速度。所以有些企业是包含敏感数据才使用HTTPS,其他保持HTTP。

HTTP协议存在的安全问题

HTTP协议默认采用的时明文传输,因此有很大的安全隐患。

提高HTTP协议安全的方法,对通信内容进行加密后,在进行传输。

encrypt:加密

decrypt: 解密

plaintext: 明文

ciphertext: 密文

常见的加密方式有

  • 不可逆

  • 单向散列函数: MD5、SHA等 ,现在的全新标准是SHA-3

  • 单向散列函数算出的长度和消息长度无关,无论消息是1bit , 10M , 100G ,单向散列函数都会计算出固定长度的散列值。

  • 散列函数特点:

    • 任意长度的消息,计算出来的散列值都是一样的长度。
    • 计算速度快。
    • 消息不同,散列值不同,即使两份消息只相差一个bit位,结果也千差万别。
    • 单向性,计算之后的散列值无法还原成明文。
  • 名称

    • 单向散列函数也被叫做: 消息摘要函数,哈希函数
    • 输出的散列值也被叫做: 消息摘要,指纹

    单向散列如何防止被篡改?其实就是将源文件生成散列值保存起来,如果发现后面的文件生成的散列值和源文件的散列值不一样,说明文件被篡改了。

  • 单向散列值应用:

    • 密码加密,加密之后存到数据库,即使攻击者获取到了数据库的密码,也是散列值,无法被解密。
  • 可逆

  • 可逆就是生成的密码是可以解密回来的。加密时通过一个密钥加密,解密时也使用一个密钥解密。

  • 对称加密: 加密的密钥和解密用的密钥是同一份。DES、3DES、AES等

    • 对称加密现在主流使用AES
    • 存在问题,密钥配送问题:
    • 在使用对称加密的时候,一定会遇到密钥配送的问题。因为要想肯定要在网络中发送这个密钥,那这个密钥就很有可能被监听者获取,然后在获取你的密文,就可以解密了。
    • 解决方案:
      • 事先共享密钥(私下共享,不在网络中传输)。
      • 密钥分配中心(KDC)来分配密钥。
      • 非对称加密
  • 非对称加密: 加密时的密钥和解密时的密钥不是同一份,RSA等

  • 非对称加密是如何解决对称加密种的密钥配送问题的?

    • 加密密钥: 一般是公开的,也叫做公钥。所以非对称加密也叫公钥密码。

    • 解密密钥: 由消息接收者自己保管,不能公开,也叫做私钥。

    • 公钥和私钥是一对的,不能单独生成。只有同一对公钥和私钥才能解密成功。

    解决流程: 由消息接收者,生成一对公钥和私钥,将公钥发送给消息发送者,公钥是公开的,谁都可以获取。然后发送者通过这个公钥对消息进行加密,此时即使公钥和密文被监听也不无法解密。只有消息接收者,收到密文后,使用对应的私钥解密。

    注意: 非对称加密,解密的速度比对称加密要慢很多。

  • 其它

  • 混合密码系统

    • 在非对称加密中,虽然解决了密钥会被窃听的问题,但是加密解密速度太慢。如果消息太大,时间就会很长。所以还是使用 对称加密 加密消息。
    • 混合加密: 使用会话密钥作为本次通信的临时密钥,用这个会话密钥作为对称加密的密码加密消息。然后再用消息接收者提供的公钥加密这个会话密钥。将密文和被加密的会话密钥一起发送给消息接收者。消息接收者接收到之后,需要使用私钥才能得到正确的会话密钥,才能进一步解开密文,得到明文。即使中间密文和被加密的会话密钥被监听,监听者也无法解开会话密钥。
  • 数字签名

    • 在保证消息能够安全到达接收方后。又有一个问题,如果方送方给接收方发送了一个不好的消息,发送方确狡辩说不是自己发送的。或者有人冒充发送方给接收方发送消息,该怎么证明发送者的身份呢?又如何保证发送的消息没有被篡改?

    • 签名的过程

    • 消息发送者自己生成密钥对,发送者用自己的私钥将发送的消息加密成签名,这个签名和需要发送的消息一起发送给接收方,接收方收到之后,使用发送方提供的公钥解密签名,如果解密不出来,说明这个消息不是发送方发送的,如果解密后签名消息和消息本身不一样,证明这个消息被篡改了。但这样存在一个问题,因为解密密文是使用非对称加密,速度慢,如果消息内容太大,非常不合适,所以发送发会将消息使用单向散列函数进行加密,这个不管多大的消息最后生成的都是一个固定长度的密文。发送方对这个密文进行签名。接收方接收到之后,也是使用同样的单向散列函数对消息加密,用这个加密后的密文和解出来的签名做对比,效果是一样的,而且提高了加密解密的速度。

      注意,签名和非对称加密是反的。签名使用私钥加密,公钥解密,消息发送方生成密钥对。非对称加密使用公钥加密,私钥解密,消息接收发生成密钥对。

  • 证书

    在非对称加密中,公钥是所有人都可以获取的。这是存在一个问题,如果有一个监听者,在消息发送方向消息接收者询问公钥时,攻击者将原本属于接收者的公钥改成攻击者的公钥。那么消息发送者就会使用攻击者的公钥加密明文,然后发送给消息接收者,此时攻击者监听到这个消息,因为是攻击者自己的公钥,所以攻击者可以通过自己的私钥解密出密文。攻击者的公钥是不合法的。如何解决呢?

    使用签名的效果是一致的,因为签名本身也要使用公钥,还是会出现公钥合法性问题。需要使用公钥证书。

    因为公钥必须发给别人,所以公钥会被中间人伪造。为了保证公钥合法性,所以用到了证书。

    证书里面要有个人信息,此人的公钥,因为要证明这个公钥属于此人,还有认证机构(CA)的签名。只有CA能够认定这个公钥属于此人。

    证书使用:

    ​ 消息接收者需要生成密钥对,然后将公钥发送给认证机构,这个发送过程是又自己保证安全的手段的

    ​ 认证机构收到消息发送者的公钥后,会施加自己的签名并生成证书。

    ​ 认证机构会将使用密钥加密证书,供消息发送者下载,各大CA的公钥,默认内置再来浏览器和操作系统里面。

    查看Windows已经信任的证书

    ① Windows键 + R >>> 输入**mmc**

    ② 文件 >>> 添加/删除管理单元

    ③ 证书 >>> 添加 >>> 我的用户账户 >>> 完成 >>> 确定

SSL/TLS

HTTPS是在HTTP的基础上使用了 SSL/TLS 来加密报文,对窃听中间人攻击提供合理的防护。

SSL/TLS不仅仅可以用在HTTP协议上,还可以在FTP上使用(FTPS),SMTP上使用(SMTPS)。

TLS(Transport Layer Security): 传输层安全性协议

TLS的前生是SSL(Secure Sockets Layer)“安全套接层”. SSL和TLS工作在传输层和应用层之间。

OpenSSL的常用命令:

生成私钥: openssl genrsa -out zhanghuan.key

生成公钥: openssl rsa -in zhanghuan.key -pubout -out zhanghuan.pem

可以使用OpenSSL构建一套属于自己的CA,自己给自己颁发证书,称为“自签名证书”

HTTPS的通信过程

分成3大阶段

  1. TCP的三次握手,建立数据传输的通道
  2. TLS的连接,确实如何加密,解密
  3. HTTP请求和响应,传输和响应被TLS加密的数据

TLS 1.2的连接过程

image-20221220002035582

主要步骤是10个。

图中省略了中间产生的一些ACK确认。

步骤详解携带信息

  1. Client Hello
  2. TLS的版本号
  3. 支持的加密组件(Cipher Suite)列表 (加密组件是指所使用的加密算法及密钥长度等)
  4. 一个随机数(Client Random)
  5. Server Hello
  6. TLS的版本号
  7. 选择的加密组件 (是从接收到的客户端加密组件列表中挑选出来的)
  8. 一个随机数(Server Random)
  9. Certificate
  10. 服务器的公钥证书(被CA签名过的)
  11. Server Key Exchange
  12. 用以实现ECDHE算法的其中一个参数(Server Params)
    • ECDHE是一种密钥交换算法
    • 为了防止伪造,Server Params经过了服务器私钥签名
  13. Server Hello Done
  14. 告知客户端: 协商部分结束。
  15. 目前为止,客户端和服务器之间通过明文共享了 Client Random、Server Random、Server Params
  16. 而且,客户端也已经拿到了服务器的公钥证书,接下来,客户端会验证证书的真实有效性
  17. Client Key Exchange
  18. 用以实现ECDHE算法的另一个参数(Client Params)
  19. 目前为止,客户端和服务器都拥有了ECDHE算法需要的2个参数: Server Params、Client Params
  20. 客户端、服务器都可以的操作:
    • 使用ECDHE算法根据Server Params、Client Params计算出一个新的随机密钥串: Pre-master secret
    • 然后结合Client Random、Server Random、Pre-master secret生成一个主密钥
    • 最后利用主密钥衍生出其他密钥: 客户端发送用的会话密钥、服务器发送用的会话密钥等
  21. Change Cipher Spec
  22. 告知服务器: 之后的通信会采用计算出来的会话密钥进行加密
  23. Finished
  24. 包含连接至今全部报文的整体校验值(摘要),加密之后发送给服务器
  25. 这次握手协商是否成功,要以服务器是否能够正确解密该报文作为判定标准
    1. 9-Change Cipher Spec 10-Finished
    2. 到此为止,客户端服务器都验证加密解密没问题,握手正式结束
    3. 后面开始传输加密的HTTP请求和响应

HTTP的升级

**HTTP/1.1不足: **

  • 同一时间,一个连接只能对应一个请求,就是TCP建立的通道可以复用,HTTP需要排队,需要并发需要两个TCP连接。

  • 针对同一个域名,大多浏览器只允许同时最多6个并发连接。就是访问同一个网站,一次性最多可以建立6个TCP连接。

  • 只允许客户端发起请求,一个请求只能对应一个响应。
  • 同一个会话的多次请求,头信息会被重复传输。请求头里,相同的信息被反复请求,响应。

改进

SPDY(speedy的缩写),是基于TCP的应用层协议,它强制要求是SSL/TLS。

2009年11月,Google宣布将SPDY作为提高网络速度的内部项目。

SPDY与HTTP的关系

SPDY只是修改了HTTP请求与响应的传输方式。

只需增加一个SPDY层,现有的所有服务端应用均可以不做任何修改。

SPDY是HTTP/2的前生,2015年9月,Google宣布移除SPDY的支持,拥抱HTTP/2

image-20221225171350710

HTTP/2.0

HTTP/2.0,在2015年5月正式发表,已经成为标准。

全球有越来越多的网站支持了HTTP/2。

HTTP/2在底层传输做了很多的改进和优化,但在语义上和HTTP/1.1兼容。

各种请求方法,GET,POST等都没有变,各种headers也没有改变。

如何升级到HTTP/2

  • 开发人员不需要修改任何代码。
  • 只需要升级服务器配置,升级浏览器。

HTTP/2的特性

  1. 二进制格式
  2. HTTP/1.1采用文本格式传输数据,而HTTP/2采用二进制格式传输数据
  3. 二进制格式对于协议的解析和优化扩展带来了更多的优势和可能
  4. 基本概念:
  5. 数据流: 存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一的整数ID。已建立的连接内可以双向传递的字节流,可以承载一条或者多条信息。所有的通信都在一个TCP的连接上完成,此链接可以承载任意数量的双向数据流。
  6. 帧: HTTP/2数据通信的最小单位消息: 指HTTP/2中逻辑上的HTTP消息。例如请求和响应,消息由一个或者多个帧组成。每个帧都包含帧头(会标识出当前帧所属的数据流)。来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装。

  7. 多路复用:

  8. 客户端和服务器可以将HTTP消息分解为互不依赖的帧,然后交错发送,最后再在另一端把他们重新组装起来。

  9. 并行交错的发送多个请求,请求之间互不影响
  10. 并行交错的发送多个响应,响应之间互不干扰。
  11. 使用一个连接可以并行发送多个请求和响应。

image-20221225175033613

一个TCP连接,可以同时处理多次请求和响应,这些请求和响应会被一起分成很多小的帧发送。但是HTTP的响应任然是按照请求的顺序响应的。

  1. 头部压缩:

  2. img

  3. 客户端发了两次请求,第一次请求有完整的http报文头部,第二次请求的时候只有一个path的字段不一样,但是这次报文头它只需要发送一个path的字段就好了,这样就大大减少了发送的量。这个的实现要求客户端和服务同时维护一个报文头表。上面提到的少了4kb的流量很可能是这个节省下来的。这个的意义还是很大的,因为动态请求有时候可能只需要发送几个字节的数据,但却需要发送一个几百个字节的报文头
  4. HTTP/2使用HPACK压缩请求头和响应头。可以极大减少头部开销,进而提高性能
  5. 早期版本的HTTP/2和SPDY使用 zlib压缩 ,可以将所传输头数据的大小减小85%~88%
  6. 但在2012年夏天,被攻击导致会话劫持后被更换成安全的HPACK。
  7. 实现: 客户端和服务器都有一张头信息的表,如果发现表里有的话就说明这个头信息是发过的,就会发送这个头信息对应在表中的序号。

  8. 服务器推送:

  9. 服务器可以对一个客户端的请求发送多个响应。除了对最初请求的响应外,服务器还可以向客户端推送额外资源,而无需客户端额外明确地请求。

  10. 但是也必须是有一个客户端请求的情况下才可以,没有办法在没有客户端发送请求时服务器主动响应。

HTTP/2存在的问题

  1. 队头阻塞:
  2. 在HTTP/2中,多个请求是跑在一个TCP管道中的。但当出现了丢包时,HTTP/2 的表现反倒不如 HTTP/1 了。因为TCP为了保证可靠传输,有个特别的丢包重传机制,丢失的包必须要等待重新传输确认,HTTP/2出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该TCP连接中的所有请求。而对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接(HTTP/1.1也会出现队头阻塞,但是HTTP/1.1同一个域名可以建立多个TCP连接),剩余的 TCP 连接还可以正常传输数据。
  3. image-20221225182444474
  4. 握手延迟:
  5. 在http/2的通信过程中,TCP本身就是就存在RTT(往返延迟),TCP+TLS,TLS也需要建立连接,协商会话密钥,所以往返延迟比单独的TCP还长。
  6. image-20221225183051483

HTTP/3

Google觉得HTTP/2仍然不够快,于是就有了HTTP/3

HTTP/3由Google开发,弃用TCP协议,改为使用基于UDP协议的QUIC协议实现

QUIC(Quick UDP Internet Connections),译为: 快速UDP网络连接,由Google开发,在2013年实现

于2018年从HTTP-over-QUIC改为HTTP/3。

HTTP/2中存在的两个问题,究其根本其实就是传输层使用了TCP协议导致的。为了解决这些问题,所以需要使用UDP协议来作为传输层的传输协议。为什么非要在TCP和UDP之间选择,各种折腾。Google自己开发一套新的传输协议不行吗?不行,成本太高,因为传输层的协议是定在操作系统里面的,并且全世界的通信设备都只认识TCP和UDP,重新开发传输层协议,就需要把全世界的网络设备,操作系统都换一遍,这显然不可能实现。img

HTTP/3基于UDP,如何保证可靠传输?

由QUIC来保证,在QUIC中,实现了可靠传输。发送方和接收方的QUIC来验证是否丢包,是否需要重传。

连接迁移

TCP基于4要素(源IP、源端口、目标IP、目标端口)

切换网络时至少会有一个要素发生变化,导致连接发生变化

当连接发生变化时,如果还使用原来的TCP连接,则会导致连接失败,就得等原来的连接超时后重新建立连接

所以我们有时候发现切换到一个新网络时,即使新网络状况良好,但内容还是需要加载很久

如果实现得好,当检测到网络变化时立刻建立新的TCP连接,即使这样,建立新的连接还是需要几百毫秒的时间

--

QUIC的连接不受4要素的影响,当4要素发生变化时,原连接依然维持

QUIC连接不以4要素作为标识,而是使用一组Connection ID(连接ID)来标识一个连接

即使IP或者端口发生变化,只要Connection ID没有变化,那么连接依然可以维持

比如 :

​ 当设备连接到Wi-Fi时,将进行中的下载从蜂窝网络连接转移到更快速的Wi-Fi连接

​ 当Wi-Fi连接不再可用时,将连接转移到蜂窝网络连接

HTTP/3的问题

对计算机性能要求变高

据Google和Facebook称,与基于TLS的HTTP/2相比,它们大规模部署的QUIC需要近2倍的CPU使用量

Linux内核的UDP部分没有得到像TCP那样的优化,因为传统上没有使用UDP进行如此高速的信息传输

TCP和TLS有硬件加速,而这对于UDP很罕见,对于QUIC则基本不存在

随着时间的推移,相信这个问题会逐步得到改善

WebSocket

起因

HTTP的请求只能由客户端发起,所以很多网站为了实现推送技术,所用的技术都是轮询(浏览器每隔一段时间想服务器发送HTTP请求,然后服务器返回最新的数据给客户端)

轮询非常消耗服务器资源的带宽,为了能够节省带宽和资源和更实时的进行通讯,出现了WebSocket

WebSocke概念

WebSocket是基于TCP的,支持全双工通信的应用层协议。

客户端,服务器,任何一方都可以主动发消息给对方。

WebSocket和HTTP是平级关系,都属于应用层,TCP本身就是全双工通信,客户端和服务器都可以主动发消息,只是HTTP的请求应答方式限制了TCP的能力。

WebSocke握手

WebSocke需要先建立连接,这使得WebSocke成为了一种有状态的协议,之后的通信可以省略部分状态信息(HTTP需要在每个请求中携带状态信息,比如身份验证等。)

WebSocke需要使用HTTP协议来建立连接,也叫握手,由浏览器主动发起握手请求

在请求头中,设置以下信息:

image-20221231102134727

image-20221231102325155

缓存(Cache)

缓存可以提高响应速度,就是如果是同一个请求,请求资源没有发生变化。实际上并没有发起HTTP请求,是直接用浏览器本地的缓存数据展示在浏览器上。

缓存有两种:

​ 内存缓存(存储在主存上): memory cache

​ 硬盘缓存(辅存上): disk cache

​ 浏览器上的请求,出现memory cache代表这是从内存中读取的缓存

image-20221231110006500

通常会缓存的情况: Get请求 + 静态资源(比如HTML,CSS,JS,图片)

和缓存有关的响应头

Pragma: 作用类似于Cache-Control,HTTP/1.0的产物

Expires: 缓存的过期时间(GMT格式时间),HTTP/1.0的产物

HTTP是向下兼容的,所以1.0的产物也可以使用,如果和HTTP/1.1的头同时使用,旧的优先级低。

Cache-Control: 设置缓存策略,可以设置不同的值,表示不同的缓存策略

  • no-storage: 不缓存数据到本地,表示这个资源不允许缓存,每次都要从服务器重新获取
  • public: 允许用户,代理服务器缓存数据到本地,就是这个请求经过的所有服务器,包括用户自己可以将这个数据缓存下来。
  • private: 只允许用户所在的主机缓存数据到本地
  • max-age: 缓存有效时间(表示资源多长时间不过期,过期了需要重新获取),单位秒。但是过期之后,重新发起请求,如果资源没有变化,服务器返回状态码还是304,继续使用缓存。
  • no-cache: 表示这个资源可以读取缓存内容,但是每次使用缓存资源之前都要向服务器发送请求询问资源是否变化,如果没有变化,服务器响应头中,携带状态码304,不携带响应体,表示可以使用缓存,资源没有变化。如果资源有变化,服务器响应头携带状态码200,响应体内容为新的数据资源。

Last-Modified: 资源最后一次修改时间

ETag: 资源的唯一标识(根据内容计算出来的摘要值),比Last-Modified优先级高,因为Last-Modified存在问题。

和缓存有关的请求头

if-None-Match:

  • 如果上一次响应头中有Etag,就会将Etag的值作为请求头**if-None-Match**的值。
  • 如果服务器发现资源的最新摘要值和if-None-Match不匹配,就会返回资源(200 OK),否则不返回资源的具体数据(304 Not Modified)。

if-Modified-Since:

  • 如果上一次响应头中没有Etag,有Last-Modified,就会将Last-Modified的值作为请求头**if-Modified-Since**的值。
  • 如果服务器发现资源的最后一次修改时间比**if-Modified-Since**中携带的时间晚,说明资源已修改,返回资源(200 OK) , 否则不返回资源的具体数据(304 Not Modified)。

缓存的工作流程

请先仔细了解上面和缓存有关的请求头,响应头的功能。

角色: B(用户浏览器) , S(服务器) , D(需要请求的资源文件)

  1. B第一次向S发起请求,想要获取S中的D。
  2. S将D的内容放到响应体中,在响应头中通过Cache-Control设置缓存策略,

  3. 如果是no-stotage表示,不缓存,那B每次都要发起请求获取文件。(先不考虑)

  4. 如果需要使用缓存,那么设置max-age,比如希望缓存有效时间5000s,任何服务器都能缓存
  5. 那么最终得到 Cache-Control: puble,max-age=5000

3.设置Last-Modified和Etag,同时存在Etag优先级高

  • Last-Modified: 将文件最后一次的修改时间放到这里。
  • Etag: 将文件做散列函数得到一个摘要值,将这个值放到Etage中。

  • B接收到了S的响应,此时B就知道了,在这之后的5000s内,对于D这个文件,我需要缓存下来,之后的读取我都从缓存中读取,不向S发送请求获取。同时会将Last-Modified和Etag也记录下来。

  • 5000s时间到了,B想要再次获取D,需要发送请求,此次请求头中会有以下内容:

  • if-None-Match: 如果第3步的响应中有Etag,那么这次请求头就会携带if-None-Match,并设置值为Etag的值。

  • if-Modified-Since: 如果请求头中有Last-Modified,那么这次请求中就会携带if-Modified-Since,并设置值为Last-Modified的值

  • S收到了B想要获取D的请求:

  • 如有请求头中有if-None-Match,那么S就会将D再一次使用散列函数生成摘要值,如果这个摘要值和if-None-Match中的值一样,说明文件没有修改,那么不返回资源的具体数据(304 Not Modified),并且再一次设置缓存策略Cache-Control。不一样返回资源(200 OK),设置Cache-control。

  • 要是没有if-None-Match,那么就会使用if-Modified-Since,S读取D的修改时间,如果D的修改时间比if-Modified-Since晚,说明文件已修改,返回资源(200 OK),设置Cache-control。否则,不返回资源的具体数据(304 Not Modified),设置Cache-control。

  • B接收到304,就继续使用缓存,接收到200就使用新的数据,并且为新的数据设置新的缓存策略。

注意:

只要文件相同,生成的摘要是一样的,不管文件多大,都会生成一个固定长度的摘要。即使文件只有一点变化,摘要也大不相同。

Last-modified存在的问题:

  • 文件修改时候是秒级别,如果文件在1s内修改了,没有办法获取到最新数据。比如请求是 13: 20: 15来的,并且在1毫秒内返回了数据,但是在20毫秒的时候修改了文件,没法区分出来,最终还是获取使用缓存,读到了脏数据。
  • 还有就是资源被删除,但是又还原,然后保存,文件内容没有变化,修改时间确实变了,这是也会更新,但是没有必要,文件内容没有改变。比如你无聊,剪切文件玩,将文件内容全部剪切,又粘贴回来,然后保存,文件内容没变,修改时间变了。
  • 服务器的时间和客户端时间不同步。