目录
初识TCP/IP
了解HTTP之前我们得了解一下TCP/IP协议族。我们通常所说得TCP/IP协议族是互联网相关的各类协议族的总称,而HTTP属于它内部的一个子集。还有一种说法认为TCP/IP是指TCP和IP这两种协议。
TCP/IP协议族里重要的一点就是分层。从上到下依次分为以下4层:应用层、传输层、网络层和数据链路层。
TCP/IP协议族4层模型
应用层
应用层的任务是通过应用进程间的交互来完成特定的网络应用。TPC/IP协议族内预存了各类通用的应用服务。比如,FTP(文本传输协议)和DNS(域名系统)。HTTP协议也位于该层。
传输层
传输层主要为两台计算机进程之前的通信提供数据传输服务,也就是传输应用层的报文。该层的协议有:TCP协议和UDP协议。
网络层
网络层用来处理网络上流动的数据包。数据包是网络传输的最小数据单。该层规定通过怎样的路径到达对方的计算机,并把数据包传送给对方。在计算机网络中进行通信,之间可能会经过很多网络设备或者计算机,网络层就是要选择合适的传输路线。网络层会把上一层的报文封装成数据包。该层使用的是IP协议。
链路层
用来处理连接网络的硬件部分。包括操作系统、硬件设备的驱动、网卡和光纤等物理设备。硬件上的范畴均在链路层的作用范围之内。
发送端在层与层之间传输数据时,没经过一层必须要加上该层的首部信息。反之,接收端在层与层传输时必须把首部去掉。这种把数据信息包装起来的做法叫做封装。
下面会讲一讲和HTTP密不可分的三个协议:IP、TCP、DNS
负责传输的IP协议
IP协议的作用就是把各类数据包传送给对方。为了确保传送的准确,则需要满足各类条件。其中两个重要的条件就是IP地址和MAC地址。IP地址指明了节点被分配到的地址,MAC地址是指网卡所属的固定地址。IP地址是可变换的,而MAC地址基本上不会改变。
网络通信的双方在同一局域网的情况很少,通常需要多台网络设备的中转才能连接到对方。在中转时,会利用下一站中转设备的MAC地址来搜索下一个中转目标。这时,会采用ARP协议,ARP是一种解析地址的协议,根据通信方的IP地址就可以反查出对应的MAC地址。在这个中转的过程中,那些计算机、路由器等网络设备只能获悉很粗略的传输路线,这种机制称为路由选择。
确保可靠性的TCP协议
TCP位于传输层,提供可靠的字节流服务。所谓字节流服务,就是为了方便传输,TCP会将大数据分割成以报文段为单位的数据包进行管理。而可靠性涉及到的东西就很多了,比如三次握手、超时重传、流量控制等等。这一块会放到TCP/UDP的总结博客里面讲,暂时只需要知道TCP建立连接会经过三次握手,断开连接会经过四次挥手。
负责域名解析的DNS服务
DNS和HTTP一样位于应用层。它提供域名到IP地址之间的解析服务。所谓域名就是类似于www.baidu.com这种字符串。为了方便记忆,计算机也可以被赋予域名。这样我们就可以直接通过域名访问,而不是毫无字面意义的IP地址。但是要让计算机去理解域名,就有些困难了。为了解决这个问题,就需要用到DNS服务了。DNS提供域名查IP和IP查域名的服务。
在浏览器上输入一个域名会发生什么?
最后,我们以一个常见的面试题,来总结一下在使用HTTP协议进行通信的过程中,IP协议、TCP协议和DNS服务发挥了哪些作用。
URI和URL
URL就是我们使用浏览器访问WEB网页时输入的网页地址,比如:https://www.baidu.com/。URL的全称是统一资源定位符。
而URI是统一资源标识符。URI用字符串标识某一互联网资源,而URL标识资源的地点,所以URL是URI的子集。为了让你更直观的认识URI,可以了解以下URI的格式:
登录信息为指定用户名和密码,作为访问服务器资源的登录信息,此项是可选项。服务器地址就是域名,也可以是IP。端口号也是可选项,不填的就会使用默认端口号。文件路径就是服务器上该资源的路径。查询字符串就是传入到服务器的参数。片段标识符也是可选项,它通常可以标记出已获取资源中的某个位置。
初识HTTP
请求和响应
HTTP协议的作用是用于客户端和服务器端之间的通信。通过请求和响应的交互达成通信。HTTP协议规定,请求从客户端发出,最后服务端响应请求并返回。
下面来看看请求报文的构成
方法就是请求的类型,URI上一节讲过。协议版本就是HTTP的版本号,其他的部分待会再说。下面再看看响应报文的构成
状态码能反应该次请求的结果如何,图中的200就代表请求成功。
HTTP是一种无状态的协议,即HTTP协议本身不对请求和响应之间的通信状态进行保存。虽然其本身不能保存,但是为了实现保持状态的功能,于是引入了Cookie技术。关于Cookie后面再说。
接下来详细说说前面提到的请求类型,也就是方法的意义。下面介绍以下HTTP/1.1中可使用的部分方法。
GET:获取资源
GET方法用来请求访问被URI识别的资源,如果请求的是文本图片这些资源,那就保持原样返回。如果请求的是接口,那么就返回程序执行的返回结果。
POST:传输数据
虽然我们也可以利用GET方法的参数来传输资源,但是一般还是用POST方法。虽然它们功能很相似,但是最主要的一个区别就是:GET方法的参数会显示在URI上,也就是以?号开头的。而POST方法的参数会放在主体里面。这样对于传输数据来说,显然会安全一点。
PUT:传输文件
要求在请求报文的主体中包含文件内容,然后保存到URI指定的位置。
HEAD:获得报文首部
HEAD方法和GET方法一样,只是不返回报文主体部分。用于确认URI的有效性以及资源更新的日期时间等。
DELETE:删除文件
与PUT方法相反,DELETE请求删除URI指定的资源。但是和PUT一样,它们都不带验证机制,所以一般的话也不会使用PUT或者DELETE,都可以用POST替代,然后配合程序代码来验证删除。
OPTIONS:查询支持的方法
用以查询URI指定资源支持的方法。
接着再说说前面提到的Cookie
Cookie
Cookie可以解决HTTP无状态的问题。Cookie会根据从服务端返回的响应报文中的一个叫Set-Cookie的首部字段信息,来通知客户端保存Cookie。当下次客户端再次请求该服务端时就会在请求报文中加入Cookie值。常见的场景就是登录。首先客户端发送登录请求,登录成功后服务端返回用户信息,然后客户端把用户信息存入Cookie,这样用户的登录状态就保持住了。当然,一般不会直接把用户信息存入Cookie,毕竟Cookie是存在客户端的,很不安全,一般会配合Session来使用。比如把用户信息存到服务端的Session中,给客户端返回SessionID,这样一样可以查询出用户信息。
下面展示Cookie在请求响应中的样子
HTTP报文
请求和响应之间交换的信息就被称为HTTP报文。请求方的叫做请求报文,响应方的叫做响应报文。HTTP报文大致可分为首部和主体两部分。详细的可见下图
其中,请求行包含请求的方法,请求的URI和HTTP的版本。状态行包含响应结果的状态码和HTTP版本,状态码就比如前面提到的200,它代表请求成功,更多的状态码和各种首部字段待会再详说。其他则包含Cookie等信息。
HTTP状态码
状态码的职责是客户端发送请求后,描述返回的请求结果。借助状态码我们就可以知道该次请求在服务端是否正常处理了。状态码以开头的数字主要分为5大类:
我们只要遵循状态码类别的定义,即使在服务端创建自己的状态码都没问题。如果要列举每一个状态码,数量非常繁多,下面就介绍一下具有代表性的十几个状态码。
2XX:代表成功
200 OK:表示客户端发送的请求在服务端被正常处理。
204 No Content:表示请求已被成功处理,但在返回的响应报文中没用返回资源。
206 Partial Content:表示客户端进行了范围请求,而服务器成功执行了这部分的GET请求。响应报文中包含由Content-Range指定范围的实体内容。这里说一下,此处的范围请求不是我们业务代码上的范围查询,而是HTTP的范围请求。在以前网速不是很快的情况下下载一个资源,可能会发生网络中断,如果网络恢复的话就需要从头下载。这时候就会想要从上次断开的地方接着下载,那么就必须有一种请求可以支持范围请求。比如对一份10000字节大小的资源进行范围请求,可以只请求5001~10000字节内的资源。请求、响应报文的内容如下图:
3XX:代表重定向
301 Moved Permanently:永久性重定向,表示请求的资源已被分配到新的URI,以后应该使用资源现在所指的URI。
302 Found:临时性重定向,表示请求的资源已被分配到新的URI,本次请求应该使用新的URI。
303 See Other:该状态码功能上和302相同,但是303明确表示客户端应该采用GET方法获取资源。
304 Not Modified:表示资源已找到,但是不符合请求的条件。
4XX:代表客户端错误
400 Bad Request:表示请求报文中存在语法错误,需要修改请求内容后再次发送请求。
401 Unauthorized:表示发送的请求需要通过HTTP认证。当浏览器初次接收到401响应时,会弹出认证用的对话窗口。
403 Forbidden:表示请求的资源被服务器拒绝了。拒绝的理由可以在主体部分进行说明。一般是没用访问权限才会出现这个问题。
404 Not Found:表示服务器上无法找到请求的资源。
5XX:代表服务端错误
500 Internal Server Error:表示服务端执行请求时发生错误,一般是服务端代码出现了问题。
503 Service Unavailable:表示服务器暂时处于超负载或者正在进行停机维护。
HTTP报文首部
前面说到过,HTTP报文主要分为报文首部和主体。首部内容为客户端和服务端分别处理请求和响应提供所需的信息。具体的信息可以往上翻一点,看看那个截图。
HTTP首部字段是由首部字段名称和字段值构成,中间用冒号分隔。例如:
Content-Type:text/html
Keep-Alive:timeout,max=100 (多个字段值用够好分开)
首部字段主要分为4种类型:
通用首部字段,请求报文和响应报文两方都会使用的首部
请求首部字段,请求报文使用的首部字段,补充了请求的附加内容、客户端信息等信息
响应首部字段,响应报文使用的首部字段
实体首部字段,针对请求和响应报文的实体部分使用的首部字段
以上只是HTTP/1.1规范定义的47种首部字段,并不代表全部。比如常用的Cookie、Set-Cookie均未出现在其中。下面详细说说几个不好理解的字段。
Cache-Contro
该字段可以根据后面的字段值控制缓存行为,如上面的报文结构图中的no-cache代表不要缓存的资源,需要源服务器的资源。该字段还有很多其他的值可供选择,详细的大家可以另行查询。
Connection
该字段主要有两个作用:控制不再转发给代理的首部字段和管理长连接。
HTTP/1.1版本默认的连接就是长连接,之前的版本默认都是短链接。长连接的好处就是提高请求的效率。如果是短链接的话,我们每次请求资源都要进行一次TCP连接(HTTP基于TCP),也就是三次握手。请求完后断开连接,等下次请求资源再次握手。而长连接就是建立TCP连接后短时间内不断开,期间可以进行多次HTTP请求。
Via
该字段是为了追踪客户端与服务端之间的请求和响应报文的传输路径。报文经过代理服务器或网关时,会现在Via字段中附加该服务器的信息,然后再进行转发。
If-Match
带有形如If-xxx这种形式的首部字段的请求都可称为条件请求。服务器收到带有附加条件的请求首先会判断条件为真时才会执行请求。
只有当If-Match的值和Etag值一致时,服务器才会接收请求。
ETag
该字段能告诉客户端资源的唯一标识,当资源更新时这个标识也会改变。
其他的首部字段
Set-Cookie和Cookie
下图列举了Set-Cookie的字段值
Cookie的使用是非常常见的,大家可以随便打开一个网站,按F12打开调试,找到一个接口的请求,在响应首部里面应该能看到Set-Cookie字段,在请求首部应该能看到Cookie字段。Set-Cookie字段值的第一部分一般是一个字符串=另一个字符串,这就是上面的NAME=VALUE,name和value都是自定义的,然后请求时,Cookie就会带上NAME=VALUE。
DNT
该字段属于请求首部字段,DNT是Do Not Track的简称,意为拒绝个人信息被收集,是表示拒绝被精准广告追踪的一种方法。它的值只有0和1,0代表同意,1代表拒绝。
确保WEB安全的HTTPS
到此为止,我们了解到了HTTP优秀和方便的一面,它也有一些不足之处。如下:
- 通信没有加密,内容可能被窃听。
- 不验证通信方的身份,因此有可能遭遇伪装
- 无法证明报文的完整性,所以有可能被篡改
内容被窃听
由于HTTP本身不具备加密(HTTP报文使用的是明文)的功能,所以也无法作到对通信整体的加密。
由于互联网的特点,无论世界上哪个角落的服务器和客户端进行通信,在此通信线路上的某些网络设备都不可能是私有的,所以不排除在某个环节会遭到恶意的监听。
而这个窃听也并不是什么难事,比如被广泛使用的抓包工具Wireshark就可以实现。它可以获取HTTP协议的请求和响应的内容,并对其进行解析。这个软件我想应该有很多人知道,不过想要用好,还是得先学好TCP协议。
虽然HTTP协议中没有加密机制,但是可以通过和SSL或TLS的组合使用,加密HTTP的通信内容。用SSL建立安全通信线路之后,就可以在这条线路上进行HTTP通信了。与SSL组合使用的HTTP就被称为HTTPS。
身份遭遇伪装
HTTP协议的请求和响应不会对通信方进行确认,也就是说返回响应的并不一定是我们请求的服务器。而且任何人都可以向服务器发起请求。虽然使用HTTP协议无法确定通信方,但如果使用SSL则可以。SSL不仅提供加密,而且还使用了证书这种方法,来确认身份。证书一般由值得信任的第三方机构颁发,用以证明服务器和客户端是实际存在的。这些所谓的值得信任的第三机构一般是社会认可的企业或者组织机构。而且伪造证书这是一件非常困难的事情。
内容被篡改
由于HTTP协议无法证明通信报文的完整性,所以在请求或响应到达对方之前,报文的内容可能会被篡改。HTTPS又是如何发现内容被篡改的,下面就会仔细说说HTTPS的工作原理。
HTTPS工作原理
前面已经说过,HTTPS其实是一个披着SSL外壳的HTTP。而另外提到的TLS可以看作是SSL的最新版本或者升级版。
在对SSL进行讲解之前,我们先了解一下加密方法。SSL采用的是非对称加密的方式。也就是说,加密是使用的是公钥,而解密是使用的是私钥。与之对应的是对称加密,也就是加密和解密都是同一个密钥,这种加密方式,我想不用说,都应该知道,运用在HTTP中依旧不安全,因为你需要把这个密钥发送给客户端,这个期间有可能被其他人窃听到密钥。
而非对称加密,公钥是任何人都可以知道的,而私钥则只有服务器自己知道,这样客户端通过公钥加密信息,服务端通过私钥解密。看似很安全了,但是别忘了HTTP的一个缺点,就是内容可能会被篡改。客户端访问HTTPS服务器时,服务器会先把公钥发送给客户端,在这个过程中,如果有其他人篡改公钥,把公钥换成自己的,那么就失去了加密的意义。反而是服务端无法解密信息了。对于公钥被篡改这个问题,我们待会再说,先说说HTTPS到底是怎么实现加密的。
HTTPS加密方式
HTTPS采用的是对称和非对称混合式的加密机制。简单地说就是客户端得到公钥后,会生成一个简单的共享密钥,然后把共享密码用公钥加密发送给服务端,服务端解密后得到这个共享密钥,之后的通信就基于这个共享密钥进行加密通信。此时只有客户端和服务端知道这个共享密码,因为中途传输的是加密后的共享密钥,即使被窃取了,没有私钥也解不开。
再接着说前面提到的,HTTPS是如何知道服务端给客户端发送公钥时,公钥有没有被篡改。前面提到了证书,是由一些机构颁发的,一般称为CA机构。这些机构一般会有自己的私有密钥,它会对我们服务器的公开密钥做数字签名,然后将这个已签名的公开密钥放入公钥证书绑定在一起。到时候服务端往客户端发送公开密钥时,会把密钥和数字签名一起发送给客户端。然后客户端会根据CA机构的公钥对这个数字签名进行验证,以确保公钥没有被篡改。但是你会发现,CA机构的公钥是什么鬼,期间并没有发送这个东西。一般这些CA机构的公钥会内置在操作系统或者浏览器里,所以不需要传输,这样也确保了安全性。
资料:《图解HTTP》