随着互联网的发展,网络已经愈来愈普及了,绝大多数的网络请求都是基于HTTP协议的,所以在开发中,了解HTTP的基本原理是必要的,在TCP/IP四层体系结构中,HTTP协议位于应用层,它是应用层主要使用的协议,应用层往下一层就是运输层,HTTP在运输层采用的是TCP协议来保证可靠传输,知道这些后,接下来详细介绍一下 Http。html
咱们先来简单了解一下 HTTP 协议的历史演变:git
长链接和多路复用的区别算法
上面讲到HTTP/1.1的长链接和HTTP 2.0的多路复用都是复用TCP链接,它们之间有什么区别呢?在讲解它们的区别以前先来聊一聊HTTP/1.1的长链接,HTTP/1.1的长链接能够分为非流水线和流水线工做方式:数据库
如图:api
因此你会发现非流水线工做方式的长链接的若干个请求只能排队发送,后面的请求等待前面请求的响应返回才能得到执行机会,一旦有某请求处理超时,后续请求只能被阻塞,这样会致使TCP链接空闲,浪费资源;而流水线工做方式的长链接就能够连续的发送多个请求而没必要等待响应的返回,这样看起来效率提升了一些。浏览器
因此流水线工做方式就是好的吗?这里根据我查到的资料是:Pipelining在现代浏览器中是默认关闭的,由于因为技术和安全的关系,实现Pipelining在HTTP/1.X上是很复杂,同时Pipelining技术不支持全部的请求方法,并且就算开启了,因为队头阻塞的存在,它的效率也不会提升多少,假设多个请求按顺序 (现实状况可能因为网络拥堵不会按顺序到达) 到达服务端,多个请求会在服务端缓存那边排队,若是第一个请求处理时间过长,会致使后面的请求阻塞,并且颇有可能后面有优先级更高的请求,这样致使高优先级请求不能被优先处理,因此无论有没有流水线工做方式,请求仍是要排队处理,队头阻塞仍是存在,流水线工做方式只能说是对非流水线方式的一种改进。缓存
那么就没有办法提升响应效率了吗?为了解决这些问题,HTTP1.x支持在同一个域名(host)下创建多个TCP链接,现代浏览器支持同时创建5~10个TCP链接,即支持并行发送请求,每一个浏览器都有一个Max-Connection最大链接限制,例如谷歌浏览器的Max-Connection值为6,即同一个域名下最多创建6个TCP链接,一次最多同时发送6个请求,超过限制后续请求就会被阻塞。安全
因此,综上所述,因为Pipelining技术在浏览器中默认是关闭的,因此HTTP1.1长链接的工做方式是非流水线形式,为了实现并行发送请求只能经过创建多个TCP链接,这在HTTP1.x时代中已经很不错了,可是随着技术的发展,如今一个网站每秒简简单单就上百个请求,而频繁的创建和保留TCP链接又是一件工做量不小的工做,因此并行TCP链接带来的效率有时也会很低。服务器
因此为了解决以上全部问题,HTTP/2.0就诞生了,HTTP/2.0的多路复用(multiplexing)才是作到了同一个TCP链接下的真正的并发请求,多个请求可同时在一个TCP链接上并行发送,某个请求任务超时,不会影响到其它链接的正常执行,并且每一个请求均可以分配优先级,优先级高的先执行。cookie
如图:
HTTP/2.0从协议的层面改进了HTTP,是将来的应用,对于HTTP2.0的更多优势能够查看HTTP2.0新特性, 因为HTTP/2.0还未大规模应用,因此下面的讨论都是围绕HTTP/1.x。
HTTP1.1的长链接在浏览器中是默认开启的,经过指定首部Connection:Keep-Alive,后面会讲到长链接的工做原理。
HTTP是基于TCP的应用层协议,从更高层次封装了TCP的使用细节,使得网络操做更为简单,一个HTTP请求就是一个典型的C/S模式,HTTP协议首先要和服务端创建TCP链接,当创建TCP链接的三报文握手的前两次报文握手完成后,在第三次握手,客户端就把HTTP请求报文做为第三个握手报文的数据发送给服务端,服务端收到请求报文后,就把所请求的文档做为响应报文返回给客户端,以下:
HTTP的工做特色能够总结为如下3点:
HTTP协议提供了几种请求方式,你们熟知的请求方式有8种GET、POST、DELETE、PUT、HEAD、TRACE、OPTIONS、CONNECT,其中最经常使用的是PUT(增)、DELETE(删)、POST(改)、GET(查)。下面以一张表来看看它们各自的做用。
请求 | 做用 |
---|---|
GET | 获取资源:客户端经过URL获取服务端中的某个资源,请求参数放在URL中,而后服务端返回对应资源给客户端 |
POST | 传输实体主体:POST请求一般会用来提交HTML表单,把数据填在表单中,传给服务器,而后服务器对这些数据进行处理,虽然GET方法也能够用来传输主体实体,可是通常采用POST方法 |
PUT | 传输文件:与GET相反,PUT向服务器写入数据,通常用来传输文件,把须要传输的文件放在请求报文的主体上,而后保存到URL指定的位置 |
DELETE | 删除文件:与PUT相反,DELETE请求求服务器删除URL所指定的资源,请求参数放在URL中,可是服务端能够在客户端不知情下撤销此请求 |
HEAD | 获取报文首部:HEAD与GET相似,但服务器在响应中只返回首部不会返回主体部分,HEAD是用来在不获取资源的状况下获取资源的首部进行检查,如查看响应的状态码,看看资源是否被修改,对象是否存在 |
TRACE | 追踪路径:客户端发起一个请求时,可能要穿过防火墙,代理,网关等,每个中间点都会修改HTTP原始请求报文,TRACE容许请求最终发送给服务端时,看看它最终变成什么样,服务端会返回一个状态码200 OK的响应报文,报文主体包含了TARCE信息 |
OPTIONS | 询问支持的方法:OPTIONS询问服务端支持的用来查询指定URL资源的方法,这就让客户端不用访问那些实际的资源就能断定访问各类资源的最优方法 |
CONNECT | 要求使用隧道协议链接代理:CONNECT要求在与代理服务器通讯时创建隧道,实现用隧道进行TCP通讯,隧道就是通过加密的通讯信路,通常使用SSL/TLS协议把通讯内容加密后经隧道传输 |
HTTP/1.0支持的方法有:
GET、POST、PUT、HEAD、DELETE;
HTTP/1.1新增的方法有:
OPTIONS、TARCE、CONNECT。
代理服务器:代理服务器是一种具备转发功能的服务器,它扮演了服务器和客户端之间的中间人角色,它能够接收客户端发来的请求并转发给服务端,也能够接收服务端返回的响应并转发给客户端,在这个过程当中它不会改变任何URL,报文每通过一个代理服务器,都须要在首部via字段的末尾插入一个能够表明代理服务器的独特的字符串, 代理服务器主要做用有:缓存代理(减小网络带宽)、访问控制(提升安全性)等。
用于HTTP协议交换的信息称为HTTP报文,客户端发出的HTTP报文叫作请求报文,服务端返回的HTTP报文叫作响应报文,它们都是由多行数据构成的字符串文本,用CR + LF (回车符 + 换行符) 做为换行符,HTTP报文大致分为报文首部和报文主体两块,由第一个出现的空行(CR + LF)划分,其中报文主体不是必须的,以下:
其中报文首部又能够分为:开始行、首部行;开始行根据报文的不一样又分为:请求行、状态行;首部行根据报文的不一样与首部字段的做用又能够分为:请求首部字段、响应首部字段、通用首部字段、实体首部字段。
下面分别简单介绍一下HTTP的请求报文和响应报文:
一个HTTP的请求报文一般由请求行,请求首部,空行(CR + LF),请求主体4个部分组成,如图:
请求行
又叫起始行,就是报文的第一行,在请求报文中说明要以什么方式作什么请求;
请求首部
又叫首部,在请求行以后,由零个或多个首部字段组成,每一个字段包含一个key和value,用冒号 : 分割,如Connection:keep-Alive,每一个首部字段以一个CR + LF结束;
请求主体
又叫主体,其中能够包含任意类型的数据,如图片,视频、文本等,而请求首部和请求行只能是文本形式,在请求主体中包括了要发送给Web服务端的数据。
不一样的请求方式,它们的请求报文格式可能有点差异的,有些请求方式它的请求主体为空,有些则不为空,可是请求行和请求首部是必须存在的,下面以GET、POST请求报文举例:
对于GET方法来讲,它全部的请求参数都是拼接在URL最后,第一个参数前经过"?"链接,而后请求参数按照"key=value"格式进行追加,每一个请求参数之间经过"&"链接,如 :
这个URL对于GET请求表示获取 www.myhost.com/text/ 位置下用户id为1,名为rain的文本,相应的请求报文格式以下:
GET /text/?id=1&name=rain HTTP/1.1
Host: www.myhost.com
Cache-Control: no-cache
复制代码
从上面的HTTP请求报文格式知,第一行为请求行,代表请求方式为GET,子路径为 /text/?id=1&name=rain,HTTP版本为1.1,后两行为请求首部,Host为主机地址,Cache-Control为no-cache,表示客户端不接受服务端缓存过的资源,而GET的请求参数都在URL中,因此请求主体为空。
注意:对于URL的最长长度,不一样的浏览器又不一样的限制,大约为1024字节(1KB)。
对于POST方法来讲,它们的报文格式通常是表单格式,也就是说请求参数存储在请求主体位置上,以下:
POST /local/ HTTP/1.1
Host: www.myhost.com
Accept-Encoding:gzip
Content-Length: 222222
Content-Type: multipart/from-data;boundary=dRGP2cPPTxE6WRTssnh4jC7HJLcSde
Connection:Keep-Alive
--dRGP2cPPTxE6WRTssnh4jC7HJLcSde
Content-Disposition:from-data;name=“username” //name = username
Content-Type:text/plain:charset=UTF-8
Content-Transfer-Encoding: 8bit
rain //value = rain
--dRGP2cPPTxE6WRTssnh4jC7HJLcSde
Content-Diaposition:from-data:name="image" //name = image
filename="/storage/emulated/0/image/1234.png"
Content-Type:application/octet-stream
Content-Transfer-Encoding:binary
//...省略二进制数据 //value = 二进制数据
--dRGP2cPPTxE6WRTssnh4jC7HJLcSde--
复制代码
上述的请求报文的含义是向 www.myhost.com/local/ 这个地址发送一个POST请求,接受的内容编码方式为gzip,请求的数据长度为222222字节,请求的数据格式为 multipart/from-data(表单),报文的boundary值为dRGP2cPPTxE6WRTssnh4jC7HJLcSde,Keep-Alive为开启长链接,空行以后,接下来就是请求报文的主体,主体有两个请求参数:
一个是名为username,值为rain的文本;
一个是名为image,值为二进制数据的图片.
请求参数是以两横杠+boundary开始的,而后是请求参数的一些首部,又称实体首部字段,如参数名,格式等,而后加上一个空行,最后才是参数的值,如上述的username=name,其表示以下:
--dRGP2cPPTxE6WRTssnh4jC7HJLcSde //两横杠加boundary做为参数的开始
Content-Disposition:from-data;name=“username” //name = username
Content-Type:text/plain:charset=UTF-8
Content-Transfer-Encoding: 8bit
rain //value = rain
复制代码
当报文主体中包含多个参数时,都要遵照这种格式:每一个参数以两横杠+boundary分隔,参数首部字段与值之间有一个空行。
请求主体的最后是以两横杆+boundary+两横杠做为整个报文的结束符,如上面报文的最后一个参数 (图片二进制数据) 最后的**- -dRGP2cPPTxE6WRTssnh4jC7HJLcSde- -**,以下:
--dRGP2cPPTxE6WRTssnh4jC7HJLcSde //两横杠加boundary做为参数的开始
Content-Diaposition:from-data:name="image" //name = image
filename="/storage/emulated/0/image/1234.png"
Content-Type:application/octet-stream
Content-Transfer-Encoding:binary
//不可省略的空行
//...省略二进制数据 //value = 二进制数据
--dRGP2cPPTxE6WRTssnh4jC7HJLcSde-- //整个报文的结束符
复制代码
一个HTTP的响应报文一般由状态行、响应首部、空行(CR + LF)、响应主体组成,以下:
状态行
在响应报文中粗略的说明了报文的执行结果;
响应首部
又叫首部,在状态行以后,由零个或多个首部字段组成,每一个字段包含一个key和value,用冒号 : 分割,每一个首部以一个CR + LF结束;
响应主体
其中能够包含任意类型的数据,如图片,视频、文本等,而首部和状态行只能是文本形式,在响应主体中包含了服务端要返回给客户端的数据.
能够看到响应报文与请求报文的格式相似,最大的不一样的就是第一行用状态信息代替了请求信息,格式以下:
HTTP-Version Status-Code Reason-Phrase CRLF
复制代码
其中HTTP-Version表明HTTP协议版本,Status-Code表明响应状态码,Reason-Phrase表明状态码的文本描述,其中状态码的5种取值范围以下:
取值范围 | 含义 |
---|---|
100~199 | 信息状态码,表示请求已被接收,正在处理 |
200~299 | 成功状态码,表示请求已被成功处理 |
300~399 | 重定向状态码,表示完成请求必需要进行进一步的操做 |
400~499 | 客户端错误状态码,表示客户端请求有语法错误或请求没法实现 |
500~599 | 服务端错误状态码,表示服务端处理请求时出错 |
例如这是一个GET请求的返回的响应报文格式:
HTTP1.1 200 OK
Data:Sat, 30, Dec 2006 23:23:00 GMT
Content-Type:text/html;charset=UTF-8
Content-Length:852
<!DOCTYPE html>
<html lang="zh-CN">
//...省略文档内容
</html>
复制代码
上面HTTP响应报文表示,HTTP协议版本为1.1,响应状态码为200,表示请求成功,返回数据的类型为text/html(html), 编码为UTF-8,返回数据的内容长度为852字节,空行以后,接下来就是返回的数据,是一个html文档。
状态码的职责是当客户端向服务端发送请求时,描述服务端返回的请求结果,借助状态码,咱们就能够得知服务端是正常处理了请求,仍是出现了错误,下面是开发中常常碰见的状态码:
2XX的响应结果表示请求被正常处理了.
3XX的响应结果表示客户端须要执行某些特殊操做后,服务端才能继续处理请求.
4XX的响应结果表示客户端发生错误的缘由所在.
5XX的响应结果表示服务端发送错误的缘由所在.
更多状态码信息请访问HTTP状态码
下面列举了HTTP/1.1中的47种常见首部字段,分为通用首部字段、请求首部字段、响应首部字段、实体首部字段:
表示请求报文和响应报文双方都会使用的首部。
首部字段名 | 说明 |
---|---|
Cache-Control | 控制缓存的行为 |
Connection | 容许客户端和服务端指定与请求/响应链接相关的选项 |
Date | 建立报文的日期时间 |
Pragma | 报文指令 |
Trailer | 报文末端的首部一览 |
Transfer-Encoding | 告知接收端为了保证报文的可靠传输性,对报文采用了什么的编码方式 |
Upgrade | 升级为其余协议 |
Via | 代理服务器的相关信息 |
Warning | 错误通知 |
表示请求报文和响应报文的主体的实体部分使用的首部,主要做用是补充资源内容的更新时间与实体相关的信息,能够看到大多都是以Content开头的。
首部字段名 | 说明 |
---|---|
Allow | 资源可支持的 HTTP 方法 |
Content-Encoding | 实体主体适用的编码方式 |
Content-Language | 实体主体的天然语言 |
Content-Length | 实体主体的大小 |
Content-Location | 替代对应资源的 URI |
Content-MD5 | 实体主体的报文摘要 |
Content-Range | 实体主体的位置范围 |
Content-Type | 实体主体的媒体类型 |
Expires | 实体主体过时的日期时间 |
Last-Modified | 资源的最后修改日期时间 |
表示从客户端向服务端发送请求报文时使用的首部,主要做用是补充请求的附加内容、客户端信息、响应内容相关优先级、编码等信息。
首部字段名 | 说明 |
---|---|
Accept | 客户端可识别的内容类型列表 |
Accept-Charset | 客户端可识别的字符集 |
Accept-Encoding | 客户端可识别的数据编码 |
Accept-Language | 客户端可识别的语言(天然语言) |
Authorization | Web 认证信息 |
Expect | 期待服务器的特定行为 |
From | 用户的电子邮箱地址 |
Host | 请求的主机名 |
If-Match | 比较实体标记(ETag) |
If-Modified-Since | 比较资源的更新时间 |
If-None-Match | 比较实体标记(与 If-Match 相反) |
If-Range | 资源未更新时发送实体 Byte 的范围请求 |
If-Unmodified-Since | 比较资源的更新时间(与 If-Modified-Since 相反) |
Max-Forwards | 最大传输逐跳数 |
Proxy-Authorization | 代理服务器要求客户端的认证信息 |
Range | 实体的字节范围请求 |
Referer | 对请求中 URI 的原始获取方 |
TE | 传输编码的优先级 |
User-Agent | 发出请求的浏览器类型,能够自行设置 |
表示服务端向客户端返回响应报文时使用的首部,主要做用是补充响应的附加内容、要求客户端附加额外的内容等信息
首部字段名 | 说明 |
---|---|
Accept-Ranges | 是否接受字节范围请求 |
Age | 推算资源建立通过时间 |
ETag | 资源的匹配信息 |
Location | 令客户端重定向至指定 URI |
Proxy-Authenticate | 代理服务器对客户端的认证信息 |
Retry-After | 对再次发起请求的时机要求 |
Server | HTTP 服务器的安装信息 |
Vary | 代理服务器缓存的管理信息 |
WWW-Authenticate | 服务器对客户端的认证信息 |
下面列举几个对HTTP首部的常见使用。
从HTTP/1.1起,默认都开启了长链接保持链接特性,经过在首部指定Connection:Keep-Alive,Keep-Alive也是一个首部,简单地说,当一个网页打开完成后,客户端和服务端之间用于传输HTTP数据的TCP链接不会关闭,若是客户端再次访问这个服务端上的网页,会继续使用这一条已经创建的TCP链接,Keep-Alive不会永久保持链接,它有一个保持时间,能够在不一样服务端软件中设置这个时间。
那么,长链接是如何工做的呢?长短链接是运输层(TCP)的概念,HTTP是应用层协议,它只能说告诉运输层我打算一段时间内复用TCP通道,而没有本身去创建、释放TCP通道的能力,那么HTTP是如何告诉运输层复用TCP通道的呢?分为如下几个步骤:
Keep-Alive: timeout=5,max=100表示TCP链接空闲时最多保持5秒,长链接接受100次请求就断开,长链接虽好,可是长时间的TCP链接容易致使系统资源无效占用,浪费系统资源,因此须要有一些限制。
Connection首部除了用于管理链接外,还能控制再也不转发的首部,格式为:Connection: 再也不转发的首部,当通过代理服务器时,代理服务器会把Connection首部字段中指定的首部删除后,再把报文转发给服务端。
一个网站在服务器中可能有多种语言版本、有多份相同内容的页面,例如英文版的网页和中文版的网页,在HTTP通讯时客户端与服务端进行内容协商,让服务端返回最合适的内容给客户端,内容协商会以类型、字符集、编码、语言等方式为标准返回合适的响应资源。
客户端能够在请求报文中设定特定的Accept-XX首部字段,例如 Accept、Accept-Charset、Accept-Encoding、Accept-Language等,服务端根据这些字段返回特定的资源,这些字段的解释以下:
Accept:
示例:Accept: text/html,image/jepg;q=0.8,video/mpeg;q=0.5,*/*;q=0.1
客户端在Accept首部中列举了它支持的内容类型,服务端从这些内容类型中挑选出一个优先级最高的类型,返回这个类型的内容给客户端,内容类型通常是type/subtybe形式,例如文本类型text/html、text/plain、...,图片类型image/jepg、image/gif、...,视频类型video/mpeg、...,等,多个类型之间用逗号 , 分隔,能够用 * 通配符表示接受全部类型,经过q表示优先级,与类型用分号 ; 分隔, q值越大,优先级越高,q值得范围是(0~1),若是不指定优先级,默认优先级都是1.0,内容类型放置顺序按优先级排序;
Accept-Charset
示例:Accept-Charset: iso-8859,unicode-1-1;q=0.5
客户端在Accept-Charset首部中列举了它支持的字符集,服务端从这些字符集中挑选出一个优先级最高的字符集,返回这个字符集的内容给客户端,与Accept相同,能够经过q指定字符集的优先级,而且和Accept同样,字符集的放置顺序按优先级排序;
Accept-Encoding
示例:Accept-Encoding: gzip,compress,deflate;q=0.5
客户端在Accept-Encoding首部中列举了它支持的内容压缩格式,服务端从这些内容压缩格式中挑选出一个优先级最高的,把响应内容用这种格式压缩后再返回给客户端,客户端收到压缩的内容后,用相应的解压算法解压内容,再显示出来,与Accept相同,能够经过q指定优先级,经过 * 通配符表示支持任意压缩格式;
Accept-Language
示例:Accept-Language: zh-cn,en-us;q=0.5
客户端在Accept-Language首部中列举了它支持的语言,服务端从这些语言中挑选出一个优先级最高的,返回相应语言版本的内容给客户端,若是没有优先级最高的语言版本,就返回次优先级的语言版本,例如示例中,没有中文版时,就返回英文版。
当服务器从各类选择列表中挑选出客户端支持的类型、字符集、编码、语言后,就会在响应报文的首部指定Content-XX首部字段,告诉客户端响应内容的类型、字符集、编码、语言,例如Content-Type、Content-Encoding、Content-Language等,Content-Type表示内容类型,还会指明内容的字符集,Content-Encoding表示内容的编码类型,Content-Language表示内容的语言。
上述的内容协商叫作服务器驱动协商,即由客户端经过首部告诉服务端它支持的东西,而后服务端作出选择,可是对于用户来讲,浏览器替咱们作的决定不必定是最优的,例如我看不懂英文,浏览器根据代理地理位置作了判断,返回了英文版的内容,这然对用户来讲不是最优的,因此还有一种叫作客户端驱动协商,由用户告诉服务端他想要什么类型的内容,这时浏览器会弹出选择列表让用户选择,用户选择后,浏览器把用户的选择填入Accept-XX首部,再发送给服务端。
Cookie是用来管理客户端和服务端之间的状态,它是服务器发送到客户端并保存在本地的小型文本文件,其内容为一系列的键值对,Cookie并不属于HTTP/1.x的规范,可是因为HTTP的无状态特性,Cookie被普遍应用于各大Web网站的状态管理及用户识别。
Cookie的工做过程主要使用到了Set-Cookie和Cookie这两个首部,Set-Cookie首部存在于响应报文中,Set-Cookie首部包含服务端返回给客户端状态管理使用的Cookie信息,客户端收到响应后会从Set-Cookie首部中取出Cookie信息保存到本地;Cookie首部存在于请求报文中,Cookie首部包含客户端从服务端接收到的Cookie信息,每次客户端发起请求时,都会在请求报文的Cookie首部中携带Cookie信息发送给服务端。
Cookie须要和服务端的Session配合使用,Cookie是存储在客户端中,而Session是存储在服务器端中,Session是服务端保存用户状态的方式,它们的工做过程大概以下:
下面是我登录掘金时在响应报文找到的Set-Cookie首部,以下:
HTTP/1.1 200 OK
//...省略不少首部
Set-Cookie: ab={}; path=/; expires=Fri, 15 Jan 2021 11:13:21 GMT; secure; httponly
Set-Cookie: auth.sig=nl1rsPof1lOURBJ1F81MyhGsoxs; path=/; expires=Thu, 23 Jan 2020 11:13:21 GMT; secure; httponly
Set-Cookie: QINGCLOUDELB=8015b18e7b6ee1bafcfd11812d999975a4db71eff5b47ab5974a1647066247c5|XiBFV|XiBFO; path=/; HttpOnly
Set-Cookie: auth=eyJ0b2tlbiI6ImV5SmhZMk5sYzNOZmRHOXJaVzRpT2lKdVNYTkJVVzlOWldwMWNuSkJjamQ2SWl3aWNtVm1jbVZ6YUY5MGIydGxiaUk2SWpNMWNYZzVhME52V21wMFNuUXdVbTRpTENKMGIydGxibDkwZVhCbElqb2liV0ZqSWl3aVpYaHdhWEpsWDJsdUlqb3lOVGt5TURBd2ZRPT0iLCJjbGllbnRJZCI6MTU3OTE3MTcxNTI0OCwidXNlcklkIjoiNWI0MzcxNzNlNTFkNDUxOTFkNzljMjdhIn0=; path=/; expires=Thu, 23 Jan 2020 11:13:21 GMT; secure; httponly
复制代码
能够看到它发送了4个Cookie给我,这个4个Cookie分别为ab=XX、_auth.sig=XX、_QINGCLOUDELB=XX、auth=XX,后面的一些path=XX、expires=XX、secure、httponly都是Cookie的附加信息,待会解释。
我随便点开几篇文章,它的请求报文里的Cookie首部都会携带上面的Cookie,以下:
GET /user/5b437173e51d45191d79c27a/posts HTTP/1.1
//...省略不少首部
Cookie: ab={};auth=eyJ0b2tlbiI6ImV5SmhZMk5sYzNOZmRHOXJaVzRpT2lKdVNYTkJVVzlOWldwMWNuSkJjamQ2SWl3aWNtVm1jbVZ6YUY5MGIydGxiaUk2SWpNMWNYZzVhME52V21wMFNuUXdVbTRpTENKMGIydGxibDkwZVhCbElqb2liV0ZqSWl3aVpYaHdhWEpsWDJsdUlqb3lOVGt5TURBd2ZRPT0iLCJjbGllbnRJZCI6MTU3OTE3MTcxNTI0OCwidXNlcklkIjoiNWI0MzcxNzNlNTFkNDUxOTFkNzljMjdhIn0=; auth.sig=nl1rsPof1lOURBJ1F81MyhGsoxs;QINGCLOUDELB=7526744c262201bf8ae89c7035a8ce8c9eb2c663a78c233d245e9356cc89386b|XiBG2|XiBG2;//省略了一些其余Cookie值
复制代码
能够看到Cookie首部都携带上了ab=XX、_auth.sig=XX、_QINGCLOUDELB=XX、auth=XX这4个Cookie。
挑一个Set-Cookie值解释一下它里面每一个属性的含义,以下:
Set-Cookie: ab={}; path=/; expires=Fri, 15 Jan 2021 11:13:21 GMT; secure; httponly
复制代码
能够看到服务器端返回的Set-Cookie首部值中,每一个属性用分号 ; 分隔,这些属性的解释以下:
属性 | 解释 |
---|---|
NAME=VALUE | 表示Cookie的名称和值,上面已经说过了,它是name=value形式的,在这里Cookie为ab={},ab就是名称,{}就是值,每个Set-Cookie首部都必需含有这个,在客户端发送请求时,Cookie会放在Cookie首部中 |
path=路径 | Path属性指定了服务端下的哪些路径能够接受Cookie值,以斜杠**/** 做为路径分隔符,子路径也会被匹配,在这里path=/,表示根目录包括根目录下的全部子路径均可以接受这个Cookie值 |
domain=域名 | 虽然上面举的例子里没有domain属性,可是domain属性经常和path属性一块儿指定Cookie的做用域, domain属性指定了哪些域名的服务端能够接受Cookie,若是指定了domain属性,子域名也会包含,例如设置 domain=.example.com,则 子域名www2.example.com也可使用这个Cookie,若是不指定,默认为当前服务端,但不包含子域名 |
expires=过时时间 | expires属性表示Cookie的有效期,在服务端发送Cookie给客户端时,能够设定Cookie的过时时间,当省略expires属性时,Cookie的有效期仅维持到浏览器关闭以前,携带过时的Cookie给服务端是无效的,服务端发送过来的新的Cookie能够覆盖过时的Cookie |
secure | 含有secure属性表示仅在进行HTTPS链接时,才容许发送这个Cookie |
httponly | 含有httponly属性表示这个Cookie不能被JavaScript脚本调用,由于跨站脚本攻击 (XSS) 经常使用 JavaScript 的 document.cookie api窃取用户的 Cookie 信息,而Cookie附加了httponly属性后,document.cookie这个api就没法访问Cookie信息,从而避免了XSS,可是在Web页面内仍是能够对Cookie进行读取操做 |
跨站脚本攻击 (XSS): 是指攻击者诱用户进入圈套,由用户在不知情的状况下执行攻击代码,攻击者事先编写脚本植入到用户的浏览器页面,用户在运行这些页面时就会触发脚本,发起攻击,常见的攻击有:利用脚本窃取用户的Cookie值、利用虚假输入表单骗取用户我的信息、显示伪造的图片或文章等。
讲到HTTP不得不讲HTTPS,HTTPS并不是一种新的协议,HTTPS就是安全版的HTTP,因为HTTP设计简单,使用简单,致使了它存在了大量安全问题,主要体如今如下三个方面:
因此,为了解决以上3个安全问题,就出现了HTTPS。
HTTPS使用加密 + 完整性保护 + 认证的方法解决上述3个问题,经过在HTTP的应用层与运输层之间加了一层SSL/TLS,以下:
HTTP协议运行在TCP之上,HTTPS协议运行在SSL/TLS之上,SSL/TLS协议运行在TCP之上,使用HTTPS,全部传输的内容都要经过SSL/TLS层,加密 + 完整性保护 + 认证的工做就在SSL/TLS层中进行,换句话说HTTPS = HTTP + SSL/TLS。
SSL(Secure Socket Layer)与TLS(Transport Layer Security)都是安全性协议,TLS是以SSL为原型开发的协议,因此TLS是基于SSL,有时会把SSL和TLS统称为TLS,目前的主流使用是TLS1.二、TLS1.3。
下面简单讲解一下加密、完整性保护、认证的实现方式:
为了防止报文内容被窃听,HTTPS采用了对称加密 + 非对称加密的混合加密机制。
对称加密:算法是公开的,在对称加密算法中,加密和解密都是使用的同一个密钥,所以对称加密算法要保证安全性的话,密钥要作好保密,只能让使用的人知道,不能对外公开;
非对称加密:算法是公开的,在非对称加密算法中,加密使用的密钥和解密使用的密钥是不相同的,用来加密的密钥叫作公钥,用来解密的密钥叫作私钥,公钥是公开的,全部人均可以得到,私钥就须要作好保密,只能让使用的人知道,不能对外公开,非对称密钥除了用来加密,还能够用来进行签名,由于私钥没法被其余人获取,所以通讯发送方使用其私钥进行签名(加密),通讯接收方使用发送方的公钥对签名进行解密,就能判断这个签名是否正确.
HTTPS在通讯的时候使用对称加密,可是使用对称加密就有一个问题,如何把密钥安全的发送给对方?若是简单的经过HTTP把密钥发送给对方,就有可能被中间人截获,这样对称加密就没有意义了,因此HTTPS采用了非对称加密来发送对称加密的密钥,这个过程以下:
一、首先服务端经过非对称加密算法生成一对密钥:公钥和私钥;
二、服务端把公钥发送给客户端,私钥本身保存;
三、客户端收到公钥后,利用公钥对对称加密使用的密钥S进行加密获得T,而后再发送给服务端;
四、服务端收到T后,利用私钥解密T,获得密钥S;
五、这样客户端和服务端都拥有了对称加密使用的密钥S,在以后的通讯过程当中就使用对称加密进行。
在这个过程当中,就算中间人获得了T和公钥,想要经过公钥把T破解获得密钥S是很是困难的,以目前的技术来讲,是几乎不可能实现,因此这样比直接发送密钥安全了不少。
综上所述,混合加密使得通讯过程的安全获得保证,上述是为了讲解方便,在实际中,对称密钥不会只在客户端生成的,它同时会在服务端生成,这在后面的HTTPS的通讯过程当中讲到。
既然非对称加密破解困难,安全,为何通讯时不一直使用? 首先非对称加密的运算比对称加密的运算复杂不少,运算速度慢,因此不可能一直使用非对称加密来通讯,而对称加密的运算速度比非对称加密快,效率高,因此,HTTPS就充分利用二者的优缺点,结合使用,在交换密钥时使用非对称加密保证安全,在通讯时使用对称加密保证安全和效率。
在讲解数字签名校验以前,先来说一下HTTP的消息摘要校验,又称散列值校验。
HTTP的消息摘要校验过程是这样的:
一、服务端在发送报文以前,先用散列值生成算法生成报文的消息摘要,而后把报文和消息摘要一并发送给客户端;
二、客户端接收到报文和消息摘要后,就用相同散列值生成算法(已经协商过了)从新计算报文的消息摘要,若是从新计算的消息摘与发送来的消息摘要相同,就说明报文在中途没有被篡改过,不然被篡改过;
为何HTTP的消息摘要校验是不可靠的?假设中间人拦截了上述过程的报文,首先经过穷举法找出你所用的散列值生成算法,修改报文后,用散列值生成算法从新计算报文的消息摘要,而后替换掉本来的消息摘要,而后把修改后的报文和从新计算的消息摘要一并发给客户端,客户端按照上述相同的验证流程,会得出报文没有被篡改过的结论,但其实报文已经被被篡改过了,因此HTTP的消息摘要校验是不可靠的,HTTP的消息摘要校验不可靠主要在于中间人能够从新生成篡改后报文的消息摘要。
因此为了解决HTTP消息摘要校验的缺点,HTTPS采用了数字签名进行完整性保护,其中签名使用到了非对称加密的公钥和私钥。
因此采用了数字签名后,校验过程是这样的:
一、服务端在发送报文以前,先用散列值生成算法生成报文的消息摘要,而后再用私钥加密消息摘要生成数字签名,把数字签名与报文一块儿发送给客户端;
二、客户端接收到报文和数字签名后,先用相同的散列值生成算法从新计算报文的消息摘要,而后再用公钥解密数字签名获得报文的消息摘要,而后比较两份消息摘要是否相同,若是相同,说明报文在中途没有被篡改过,不然被篡改过;
HTTPS的数字签名是如何保证可靠性的?假设中间人截获了报文,把报文修改后从新生成消息摘要,可是中间人没有私钥对生成的消息摘要进行签名,由于私钥是保密的,这样他就没法从新生成新的数字签名,因为中间人没有办法生成修改后报文的数字签名,因此这就保证数字签名的可靠性,接收方收到数字签名和报文后,经过数字签名的校验流程,就能判断出报文的正确性。
综上所述,数字签名使得消息的完整性获得保证,在HTTPS中,数字签名通常会用到数字证书的传递上,下面会讲。
为何HTTPS要生成报文的消息摘后,再对消息摘要进行签名,而不对报文直接签名? 这是由于报文内容通常都很长,而报文的消息摘要输出的长度是固定,比报文长度短,这样经过私钥进行加密的运算量就大大减小,提升效率,因此当非对称加密与消息摘要结合使用后,便造成了一种高效又安全的数字签名方案。
你们有没有发现,前面所讲的数字签名和混合加密技术,客户端都必须事先知道服务端的公钥,若是一开始公钥就被中间人篡改了,那么坏人就会被你当成好人,你就会拿着这把假的公钥和假的服务端通讯,因此如何保证公钥是真正的服务端颁发,又是另一个问题,为了保证公钥的安全可信,HTTPS经过数字证书来解决服务端的身份认证问题。
数字证书(Digital Certificate):数字证书是由数字认证机构(Certificate Authority, 简称CA)颁发的公开密钥证书,它里面大概包含以下信息:
一、发布机构(Issuer): 表示该证书是由哪一个机构(CA)颁发的;
二、有效期(Validity): 表示证书的使用期限,过了有效期,证书就失效了
三、名称(Subject):表示证书全部人的名字,这个证书是发给谁的,通常是某我的或者某个公司名称、机构的名称、公司网站的网址等;
四、公钥(Public-Key): 表示证书全部人想要公布出去的公钥;
五、签名算法(Signature algorithm): 表示证书的数字签名所使用的加密算法,这样就可使用证书发布机构的证书里面的公钥,根据这个算法对数字签名进行解密;
六、数字签名(Digital Signature):表示证书发行者CA对该证书的数字签名,用于保证数字证书的完整性,确保证书没有被修改过.
数字认证机构是处于客户端和服务端双方都信赖的第三方机构,由CA颁发的数字证书必定是可靠、可信的,下面来简单介绍一下服务端向CA申请数字证书的流程:
一、服务端的运营人员向CA提交本身的公钥、组织信息、域名等信息,而后CA会经过各类渠道、各类手段来判断服务端的身份是否真实,是否合法等(在这里就能够杜绝中间人非法申请证书);
二、服务端的身份审核经过后,CA就会把服务端的公钥和证书的其余信息经过散列值算法生成一个消息摘要,而后用CA的私钥对消息摘要进行签名生成数字签名,而后把数字签名放入证书中,而后把这个证书颁发给服务端,因此数字证书最终包含服务端公钥 + 证书的数字签名 + 证书的其余信息,以下:
如今服务端拿到了数字证书,客户端第一次请求服务端时,服务端就会把这个数字证书发送给客户端,客户端收到数字证书后,就会用CA的公钥对数字证书进行数字签名的验证,若是验证经过,说明数字证书中途没有被篡改过,是可靠的,从而知道数字证书中的公钥也是可靠的,因此客户端就放心的取出数字证书中的公钥进行之后的HTTPS通讯。
在对数字证书的数字签名进行验证以前,必须先知道CA的公钥,由于数字证书的数字签名是由CA的私钥进行签名的,因此CA的公钥必须安全的转交给客户端,如何安全的转交又是一个困难的问题,因此,大多数浏览器开发商发布版本时,会事先在内部植入经常使用的CA机构的根证书, 这些根证书中会包含CA的公钥,也就是说CA的公钥已经内置在浏览器中了。
综上所述,数字证书能够确认服务端的身份,能够解决公钥的安全发放问题,同时数字证书也是经过数字签名来验证的。
浏览器内置的CA都是经常使用的、信任的CA机构,因此若是服务端发来的数字证书的相关CA机构恰好不在浏览器的内置CA列表中,浏览器就会找不到该数字证书的CA公钥,就会断定该数字证书是非法,这时浏览器会提示你手动安装该数字证书的CA机构的根证书,这个时候你就要本身承担风险了,颇有可能这个网站是不可信任的,安装了根证书后就能够拿到CA的公钥进行数字证书的验证,验证经过后就能与服务端通讯。
打开一个由HTTPS链接 (地址栏上有一个锁的标志) 的网站,经过如下方式查看它的数字证书,以下:
在进行HTTPS通讯前,必须先进行TCP三次握手创建TCP链接,而后进行TLS握手,进行完TLS握手后才会开始加密的HTTPS通讯,在TLS握手的过程当中主要进行密钥交换(对称加密使用的密钥)、身份认证等步骤,根据密钥交换时使用的算法不一样,TLS握手能够分为RSA握手和DH握手,目前主流的是DH握手,并且RSA握手因为它没有向前保密,已经在TLS1.3中被淘汰了,关于这两个握手算法的主要细节与区别能够查看下面文章,限于篇幅,本文不展开讨论:
Keyless SSL: The Nitty Gritty Technical Details
我经过抓取各大主流网站的TLS包发现,目前HTTPS使用的TLS版本几乎都是TLS1.2和TLS1.3版本,下面的分析都是基于TLS1.2的DH握手过程,下面是我打开第一次打开csdn博客时抓取的TLS包(通过过滤后),以下:
其中info栏的信息表示TLS握手过程当中客户端和服务端之间交互时发送的TLS包的名称,有Server Key Exchange包表示它本次的握手类型是DH握手,上面的TLS握手过程能够用下图表示,以下:
上图省略了New Session Ticket包,New Session Ticket包是用于会话复用,并非必要的,省略它并不会影响对TLS握手过程的理解,在TLS握手完成以前,报文的传输都是明文,TLS握手过程的每一个步骤的解释以下:
若是客户端和服务端都成功解密了最后那个Encrypted Handshake Message报文,证实客户端和服务端的对称密钥生成完毕,TLS握手都所有完成,接下来双方均可以经过对称密钥进行加密通讯,Application Data报文中的数据就是加密后的HTTP报文,当HTTPS通讯结束后,由客户端主动发出Client Close Notify报文断开链接。
由上面DH握手过程能够看出,对称密钥是根据一些参数在各端生成,并非在客户端生成后经过公钥加密传输给服务端,这样作是为了保证服务端的私钥不和对称密钥有关联,什么意思呢?若是在客户端生成密钥经过公钥加密传输给服务端,服务端能够由私钥解密出密钥,这样私钥就参与了对称密钥的解密,到也就是说,只要我拥有服务端的私钥,我就能够解密出密钥,因此若是中间人把你TLS握手过程当中的全部报文拦截、保存,直到某一天服务端的私钥泄漏了,中间人就可使用私钥在保存的报文中解密出密钥,这样,中间人就轻松的解密出客户端和服务端之后的通讯内容,而经过DH参数的交换就能够避免这个漏洞,为何DH交换就能够? 这归根究竟是一个数学问题,你们能够自行查找资料。
你们只须要知道在TLS握手中,对称密钥是服务端和客户端各自生成的,服务端公钥和私钥的功能被削弱到用来进行身份认证或者签名验证,这么作的目的都是为了保证通讯的向前保密、安全。
HTTPS这么安全,为何不全部网站都使用? 一、效率问题:与HTTP的明文通讯相比,加密通讯须要消耗更多的CPU资源与内存; 二、部署问题:使用HTTPS须要有权威CA的证书颁发,从证书的选择、购买到部署都是一个耗时耗力的过程; 三、成本问题:购买证书也是一笔开销.
网络请求已经成为了一个应用最基本的部分,因此熟悉HTTP对于咱们开发很重要,咱们不只会用开发环境提供给咱们的API,还要属性它的原理,本文从发展历史、工做特色、报文格式、状态码、常见首部、HTTPS这几个方面总结了一番HTTP,固然,HTTP确定不止这一些,一篇文章是没法讲完的,限于篇幅,还有HTTP的缓存机制没有讲,这也是很重要的内容,掌握以上这些足够日常使用了。
以上就是本文所有内容,但愿你们有所收获。
参考资料:
图解HTTP