目录
HTTP协议
HTTP协议的工作过程
首行
请求头(header)
HOST
Content-Length编辑
User-Agent(简称UA)
Referer
Cookie
空行
正文(body)
HTTP响应详解
状态码
报文格式
HTTP响应格式
如何构建HTTP请求
form
Ajax(阿加克斯)
Postman
HTTPS
加密
对称加密
非对称加密
证书
HTTP (全称为 "超文本传输协议") 是一种应用非常广泛的应用层协议。HTTP往往是基于传输层的 TCP 协议实现的。(HTTP1.0,HTTP1.1,HTTP2.0均为TCP,HTTP3 基于 UDP 实现) 目前我们主要使用的还是 HTTP1.1 和 HTTP2.0(本文基于HTTP1.1来介绍)。我们平时打开一个网站, 就是通过 HTTP 协议来传输数据的。
当我们在浏览器中输入一个 搜狗搜索的 "网址" (URL) 时,浏览器就给百度的服务器发送了一个 HTTP 请求,搜狗的服务器返回了一个 HTTP 响应。
这个响应结果被浏览器解析之后,就展示成我们看到的页面内容。(这个过程中浏览器可能会给服务器发送多个 HTTP 请求,服务器会对应返回多个响应,这些响应里就包含了页面 HTML,CSS,JavaScript,图片,字体等信息)。
当我们在浏览器中输入一个 "网址",此时浏览器就会给对应的服务器发送一个HTTP 请求,对方服务器收到这个请求之后, 经过计算处理, 就会返回一个HTTP响应。
HTTP协议的交互详细过程,可以借助第三方工具来看到,被称作“抓包”工具。抓包工具有很多,我们在这里使用Fiddler(费德乐)。
Fiddler本质是一个代理程序,可能和别的代理程序冲突,使用的时候要关闭别的代理程序。
想要正确抓包,还需要开启https功能,需要手动启用一下https并且安装证书~
双击这个需要抓包的网页,进入到raw中,我们查看这个http请求最原始的效果。点击View in Notepad可以在记事本中打开。
当前的http请求,其实是一个行文本格式的数据。相比于tcp这种二进制格式来说,更加方便用户直接观察。
HTTP请求可以认为是四个部分:
首行里有HTTP的方法、URL、版本号
方法有很多,但是实际开发中,最常见的就是两个方法:GET、POST。
如果是GET请求,没有body。但是如果是POST请求,一般都有body。并且POST的body是程序员自定义的内容。而POST最典型的就是登录,登录跳转的时候会涉及到POST,另一个就是上传文件。
GET和POST之间的典型区别:
1.GET可以给服务器传递一些信息,GET传递的信息一般都是放在query string,POST传递消息一般都是在body(也可以完全使用GET提交,使用POST获取,大多是使用习惯上的区别)
2.GET请求一般都是用于从服务器获取数据,POST一般是用于给服务器提交数据。
3.GET通常会被设计成幂等,POST则不需要。(每次输入的都是同样的数据,那么输出的结果每次也都一样就是幂等)
4.GET可以被缓存,POST则一般不能被缓存(就是把请求的结果保存下来,下次请求就不必真请求了,直接取缓存结果了)
URL:
对于一个网址:https://www.baidu.com/
URL,也就是唯一资源定位符,标识互联网上的唯一的资源的位置,也就是这个资源在哪个服务器的哪个目录下的哪个文件。
URL最关键的四个部分:
1.域名/IP
2.端口号
3.带层次的路径
4.查询字符串
同时,一个URL的几个部分是可以省略的,以百度来说,https://www.baidu.com/
省略了端口,此时浏览器会提供默认端口,http默认是80,https默认是443。
URL 中的可省略部分
协议名: 可以省略,省略后默认为 http://。
ip 地址 / 域名: 在 HTML中可以省略(比如 img, link, script, a 标签的 src 或者 href 属性)。省略后表示服务器的 ip / 域名与当前 HTML 所属的 ip / 域名一致.
端口号: 可以省略。省略后如果是 http 协议,端口号自动设为 80;如果是 https 协议,端口号自动设为 443。
带层次的文件路径: 可以省略,省略后相当于 / 。有些服务器会在发现 / 路径的时候自动访问 /index.html
查询字符串: 可以省略
片段标识: 可以省略
HTTP/1.1: HTTP的版本号,这里是1.1
请求头中包含了许多内容:
大概描述了服务器所在的地址和端口,HOST这里的地址和端口,用来描述最终要访问的目标。
这个内容大概率和URL是一样的,也可能不同~
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0
UA描述了浏览器和操作系统的版本,现在的UA主要用于区分PC端网页和移动端网页~
当前页面的来源,如果直接通过地址栏输入地址或者是直接点击收藏夹都没referer。
这是非常重要的header属性,本质上是浏览器给网页提供的本地存储数据的机制。因为网页默认是不允许访问计算机的硬盘的,cookie浏览器对于访问硬盘做出了明确的限制,并且通过键值对的方式来组织数据~
Cookie是从哪来的?
Cookie中的数据来自服务器,服务器会通过HTTP响应的报头部分来决定浏览器的Cookie要存什么。
Cookie是在哪存的?
存在硬盘,Cookie在存的时候,是按照浏览器+域名维度来进行细分的。不同的浏览器各自存各自的Cookie,同一个浏览器的不同的域名,对应不同的Cookie。并且Cookie的内容不光是键值对,还有过期时间,有很多网站,登陆一次后可以自动记忆登录状态,这可能是基于Cookie来实现~
Cookie要到哪去?
当浏览器保存好Cookie之后,后续再给服务器发送请求的时候,就会自动带上这样的Cookie~
Cookie就像是服务器在浏览器中搞的寄存处一样~
正文中的内容格式和 header 中的 Content-Type 密切相关。
1) application/x-www-form-urlencoded
2) multipart/form-data
3) application/json
状态码描述了这次的响应结果。
常见的状态码:
200 OK
这是一个最常见的状态码,表示访问成功。
404 Not Found
没有找到资源。
浏览器输入一个 URL,目的就是为了访问对方服务器上的一个资源。如果这个 URL 标识的资源不存在,那么就会出现 404。
例如,在浏览器中输入 www.sogou.com/index.html,此时就在尝试访问 sogou 上的 /index.html 这个资源。如果输入正确,则可以正确访问到。但是如果输入错误,比如 www.sogou.com/index2.html,就会看到 404 这样的响应。
403 Forbidden
表示访问被拒绝。有的页面通常需要用户具有一定的权限才能访问(登陆后才能访问)。如果用户没有登陆直接访问,就容易见到403。
405 Method Not Allowed
前面我们已经学习了 HTTP 中所支持的方法,有GET、POST、PUT、DELETE等。但是对方的服务器不一定都支持所有的方法(或者不允许用户使用一些其他的方法)。
500 Internal Server Error
服务器出现内部错误. 一般是服务器的代码执行过程中遇到了一些特殊情况(服务器异常崩溃)会产生这个 状态码。
504 Gateway Timeout
当服务器负载比较大的时候,服务器处理单条请求的时候消耗的时间就会很长,就可能会导致出现超时的情况。
302 Move temporarily
临时重定向:302这样的相应报文中,会在leader里带有一个Location属性,通过这个属性来描述要跳转到哪个新的地址。
举个例子,当我们申请了一个域名后,过段时间需要更换域名,此时访问老域名通过重定向就能够访问老域名自动跳转到新的域名~
这么多状态码可以分成几类:
2开头:成功
3开头:重定向
4开头:客户端错误
5开头:服务器错误
相应的格式和请求的格式基本相同,具体的内容有些许不同。
响应报头:响应报头的基本格式和请求报头的格式基本一致. 类似于 Content-Type , Content-Length 等属性的含义也和请求中的含义一致。
Content-Type 响应中的 Content-Type 常见取值有以下几种:
text/html : body 数据格式是 HTML
text/css : body 数据格式是 CSS
application/javascript : body 数据格式是 JavaScript
application/json : body 数据格式是 JSON
响应正文:正文的具体格式取决于 Content-Type。
1) text/html
2) text/css
3) application/javascript
4) application/json
我们构建的这个请求没有特殊的需求,只是访问百度的主页。之后咱们自己写服务器代码就可以根据需要来获取url中的query string,从而完成不同的功能。
通过抓包可以获取到raw文本,我们构建的http请求除了首行之外剩下的部分都是浏览器自己添加的。
也可以构造一个HTTP POST请求。
对于form构造的post请求来说,body里面的数据格式和query string是非常相似的,也是键值对结构,键值对之间使用&来分割,键和值之间使用=来分割。
要注意,form标签只能构造GET和POST,无法构造PUT、DELETE、OPTIONS等方法的请求。
这是浏览器提供的一种通过js构造http请求的方式。Ajax全称Asynchronous Javascript And XML,也就是异步JavaScript和XML。
简单地说,通过Ajax发起http请求,不必等待服务器相应回来,就可以立刻往下执行,当服务器的响应回来后再通过浏览器反馈到代码中。
使用Ajax技术网页应用能够快速地将增量更新呈现在用户界面上,而不需要重载(刷新)整个页面,这使得程序能够更快地回应用户的操作。
和form相比,Ajax功能更强:
1.支持PUT、DELETE等方法。
2.Ajax发送的请求可以灵活设置header。
3.Ajax发送的请求的body也是可以灵活设置的。
我们主要通过jquery提供的Ajax来在代码中使用Ajax,这个api针对原生的api封装过,所以使用起来比较简单。
通过jquery cdn来获取到ajax源码后引入到js中,然后就看可以开始Ajax代码的编写了。
jquery中,$是一个特殊的全局对象,jquery的api都是以$的方式来引出的。在这里Ajax的参数就是一个js对象,是大括号表示的键值对。于是我们开始运行一下这个代码:
注意!这个代码直接执行只能看到构造的请求,无法获取到正确的响应~
因为发送给百度的服务器后,服务器没有正确处理我们的请求,就会出现无法获取到响应,之后我们能自己写服务器后,就可以自己给自己的服务器发请求,就能够处理了。
虽然JS本身不能直接控制操作系统的线程,但是浏览器在实现Ajax的时候在内部使用了多线程。
还有一个更加方便的构造http请求的方式,使用Postman。
可以通过手动构造的方式来完成构造,在Params添加相关的信息,点击Send就能把构建好的http请求发送到服务器上。
同时也可以通过一键构造,可以生成构造请求的代码,方便咱们在自己的程序中集成~
HTTPS 也是一个应用层协议。是在 HTTP 协议的基础上引入了一个加密层。因为HTTP 协议内容都是按照文本的方式明文传输的,这就导致在传输过程中出现一些被篡改的情况。
于是HTTP+安全层(SSL/TLS)就成为了HTTPS。
网络上如果都是明文传输数据,是非常危险的~所以就需要加密才能保证安全。
加密就是把明文(要传输的信息)进行一系列变换,生成密文。解密就是把密文再进行一系列变换,还原成明文。
在这个加密和解密的过程中,往往需要一个或者多个中间的数据,辅助进行这个过程,这样的数据称为密钥。
进行安全传输,核心就是“加密”。加密其中一种最有效的办法,叫做“对称加密”。与其类似的叫做“非对称加密”。
一个简单的对称加密:按位异或。 假设明文a = 1234,密钥 key = 8888,则加密 a ^ key 得到的密文 b 为 9834。然后针对密文 9834 再次进行运算 b ^ key,得到的就是原来的明文 1234。
同一个密钥,既可以用来加密,也可以用来解密。所以也叫做对称密钥。
由于黑客不知道密钥是什么,所以即使黑客截获了加密的数据也得不到解密后的数据。
但是服务器同时会服务很多个客户端,这些客户端都需要有不同的密钥。
比较理想的做法,就是能在客户端和服务器建立连接的时,双方协商确定这次的密钥是什么。
但是如果黑客从一开始就入侵了路由器,岂不是最开始黑客就能获取到密钥了?加密还有什么意义呢?所以我们需要再最开始双方协商密钥是什么的时候就把密钥给加密。
此时我们需要用到“非对称加密”。
非对称加密要用到两个密钥,一个叫做 "公钥", 一个叫做 "私钥"。
公钥和私钥是配对的,最大的缺点就是运算速度非常慢,比对称加密要慢很多。所以往往只是第一次商定密钥的时候使用非对称加密。
- 客户端在本地生成对称密钥, 通过公钥加密, 发送给服务器.
- 由于中间的网络设备没有私钥, 即使截获了数据, 也无法还原出内部的原文, 也就无法获取到对称密钥
- 服务器通过私钥解密, 还原出客户端发送的对称密钥. 并且使用这个对称密钥加密给客户端返回的响应数据.
- 后续客户端和服务器的通信都只用对称加密即可. 由于该密钥只有客户端和服务器两个主机知道, 其他主机/设备不知道密钥即使截获数据也没有意义.
服务器生成一对公钥私钥,客户端从服务器拿到公钥,黑客也能拿到。
客户端通过公钥给对称密钥加密,黑客获取到加密过后的秘钥却没有私钥,所以无法解开。
服务器拿到加密后的密文后,通过私钥解开,这样就得到了对称密钥。
此时客户端和服务器就可以使用这个对称密钥来进行后续数据传输了。
接下来的问题又来了:
客户端如何获取到公钥?
客户端如何确定这个公钥不是黑客伪造的?如果黑客把公钥私钥的获取过程全都替换了,该怎么办?
通过如上的方式黑客依然能够获取到通信的信息。
解决中间人攻击的关键,关键在于这个公钥是服务器的真实公钥~
此时,证书就出现了。可以理解成是一个结构化的字符串,里面包含了以下信息: 证书发布机构,证书有效期,公钥 证书所有者,签名等等~
当客户端获取到这个证书之后,会对证书进行校验(防止证书是伪造的)。于是客户端向服务器请求公钥的时候,不单单请求一个公钥,而是把整个证书都请求过来。
判定证书的有效期是否过期
判定证书的发布机构是否受信任(操作系统中已内置的受信任的证书发布机构)。
验证证书是否被篡改: 从系统中拿到该证书发布机构的公钥,对签名解密,得到一个 hash 值(称为数据摘要), 设为 hash1,然后计算整个证书的 hash 值,设为 hash2。对比 hash1 和 hash2 是否相等,如果相等,则说明证书是没有被篡改过的。
HTTP和HTTPS到这里就差不多能够解决遇到的问题了,当然,HTTP和HTTPS是非常复杂的,短短一文肯定讲不清。但是能够理解上文已经有进步了~