一 HTTP 是什么
HTTP (全称为"超文本传输协议")是一种应用非常广发的 应用层协议
当我们在浏览器中输入一个"网址",此时浏览器就会给对应的服务器发送一个HTTP请求, 对方服务器收到这个请求之后,经过计算处理,就会返回一个HTTP响应
二 HTTP 协议格式
2.1 工具的使用
HTTP 是一个文本格式的协议,可以通过Chrome 开发者工具或者FIidder ,分析 HTTP 请求/响应的细节.
• 左侧窗口显示了所有的HTTP请求/响应,可以选中某个请求查看详情
• 右侧上方显示了HTTP 请求的报文内容.(切换的 Raw 标签页可以看到详细的数据格式)
• 右侧下方显示了HTTP 的响应的报文内容.(切换到 Raw 标签可以看到详细的数据格式)
• 请求和响应的详细数据,可以通过右下角的 View in Notepad 通过记事本打开
2.2 工具的原理
Fiddler 相当于一个"代理"浏览器访问sogou.com时,就会把HTTP 请求先发给Fiddler,Fiddler 再把请求转发给 sougou 的服务器.当 sougou 服务器返回数据时,Fiddler 拿到返回数据,再把数据交给浏览器.因此,Fiddler 对于浏览器和 sougou 服务器之间交互的数据细节,都是非常清楚的.
2.3 结果
以下是一个HTTP 请求/响应的结果
HTTP 请求
• 首行:[方法] + [url] + [版本]
• Header: 请求的属性,冒号分割的键值对;每组属性之间使用 \n 分隔;遇到空行表示 Header 部分结束
• 空行:Header的结束标记
• Body:空行后面的内容都是Body. Body 允许为空字符串.如果Body 存在.则在Header 中会有一个Content-Length
HTTP响应
• 首行:[版本号] + [状态码] + [状态码解释]
• Header : 请求的属性,冒号分割的键值对; 每组属性之间使用 \n 分割;遇到空行表示 Header 部分结束
• 空行: Header 的结束标记
• Body : 空行后面的内容都是Body. Body 允许为空字符串. 如果Body 存在, 则在 Header 中会有一个Content-Length 属性标识Body 的长度;如果服务器返回了一个html 页面,呢么html 页面内容就是在body 中.
协议格式总结
为什么HTTP 报文中要存在 "空行"?
因为 HTTP 协议并没有规定报头部分的键值对有多少个. 空行就相当于是" 报头的结束标记 ", 或者是"报头和正文之间的分隔符"
HTTP 在传输层依赖TCP 协议. TCP 是面向字节流的.如果没有这个空行 ,就会出现"粘包问题"
三 HTTP 请求(Request)
3.1 认识方法
3.1.1 GET 方法
• GET 是最常用的方法. 常用于获取服务器上的某个资源
• 在浏览器中直接输入URL, 此时浏览器就会发送一个GET 请求
• 另外, HTML 中的link, img ,script 等标签 ,也会触发GET 请求.
• 使用JavaScript 中的 Ajax 也能触发 GET 请求
GET 请求的特点
• 首行的第一部分为GET
• URL 的 query string 可以为空, 也可以不为空
• header 部分有若干个键值对结构
• body 部分一般为空(也可以不为空)
关于 GET 请求的 URL 长度问题
网上有些资料上描述: get 请求长度最多1024kb 这样的说法是错误的.
HTTP协议有 RFC 2616 标准定义, 标准原文中明确说明:"Hypertext Transfer Protoclo -- HTTP/1.1",does not specify any requirement fro URL length.
没有对URL 的长度有任何的限制
实际上URL的长度取决于浏览器的实现和HTTP服务器端的实现. 在浏览器端,不同的浏览器最大长度是不同的, 但是现代浏览器支持的长度一般都很长;在服务器端,一般这个长度是可以配置的.
3.2 POST 方法
POST 方法也是一种常见的方法.多用于提交用户输入的数据给服务器(例如登录页面).
通过 HTML 中的 form 标签可以构造 POST 请求, 或者使用 JavaScript 的 ajax 也可以构造 POST请求.
POST 请求的特点
• 首行的第一部分为POST
• URL的 query string 一般为空(也可以不为空)
• header 部分有若干个键值对结构
• body 部分一般不为空. body 内的数据格式通过 header 中的 Content-Type 指定.body 的长度由 header 中的 Content-Length 指定
经典面试题: 谈谈 GET 和POST 的区别
• 语义不同: GET 一般用于获取数据, POST 一般用于提交数据
• GET 的body 一般为空, 需要传递的数据通过 query string 传递, POST 的query string 一般为空,需要传递的数据通过body 传递
• GET 请求一般是幂等的,POST 请求一般是不幂等的.(如果多次请求得到的结果一样,就视为请求是幂等的)
• GET可以被缓存, POST 不能被缓存.(这一点也是承接幂等行).
• GET请求可以被浏览器收藏,POST不能
补充说明
• 关于语言: GET 完全可以用于提交数据,POST 也完全可以用于获取数据
• 关于幂等性: 标准建议 GET 实现为幂等的, 实际开发中 GET 也不必完全遵守这个规则(主流网站都有"猜你喜欢"功能,会根据用户的历史行为实时更新现有的结果)
• 关于安全性: 有些资料上说"POST 比 GET 更安全". 这样的说法也是不科学的,是否安全取决于前段在传输密码等敏感信息时是否进行加密,和 GET POST 无关
• 关于数据传输量: 有些资料上说"GET 传输的数据量小,POST 传输的数据量大".这个也是不科学的,标准没有规定GET 的URL 的长度, 也没有规定 POST 的 body 的长度.传输数据量多少完全取决于不同浏览器和不同服务器之间的实现区别.
• 关于传输数据类型: 有的资料上说:"GET 只能传输文本数据,POST 可以传输二进制数据".这个也是不科学的.GET 的 query string 虽然无法直接传输二进制数据, 但是可以针对二进制数据进行 url encode.
3.2 认识 URL
URL(Uniform Resource Locator 统一资源定位符).
四 认识请求"报头"(header)
header 的整体格式也是"键值对"结构
每个键值对独占一行. 键和值之间使用分号和空格分割
4.1 Host
表示服务器主机的地址和端口
大多数情况下,Host 的值和 URL 中的域名是一致的,但是也有例外,比如当我们访问的服务器,不是直接访问的,而是通过"代理"来访问的,此时Host 和 URL 可能就不一致了(Host 是我们的最终目标,URL 是当前目标)
4.2 Content-Length/Content-Type
Content-Length表示 body 中的数据长度. 单位是字节.
Content-Type 表示请求中的 body 中的数据格式.
常见的响应类型:
• application/x-www-form-unlencoded: form 表单提交的格式.此时body 的格式形如:
title=test&content=hello
键值对之间用 & 分割, 键和值之间用 = 分割
• application/json: 数据为 json 格式.body 格式形如:
{"username":"123456789","password":"xxx","code":jwte"}
其他Content-Type :
• text/html
• text/css
• application/javascript
• image/jpg
• image/png
......
4.3 User-Agent(简称 UA)
表示浏览器/操作系统的属性.形如
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.43
其中 Windows NT 10.0; Win64; x64 表示操作系统信息:64 位 win10 系统
在很久以前,由于浏览器的种类繁多,功能参差不齐,对于网站的开发来说,难度大大提升,因为对于不同的设备的浏览器,同一页面的响应结果是不同的,于是就发明了 User-Agent,就可以告诉网站服务器,当前的上网设备是什么(银河二号,还是小霸王学习机...)此时,服务器就可以根据客户端的种类,返回不同的页面.
随着时间的推移,浏览器过了快速发展的阶段了,现在的浏览其,之间的区别也越来越小了,此时,UA这个属性存在的价值就大打折扣了.
如今 UA 的一个重要用途,就是区分用户是 手机/pc/平板...
虽然 UA 能做到这一点,但是实际上,现在还有更好的方案,来解决上述问题. CSS3 中提供了"媒体查询" 功能.可以根据浏览器窗口大小, 来设置不同的样式. 这种页面开发方式, 称为"响应式布局"
4.4 Referer
表示这个页面是从那个页面跳转过来的.形如:
Referer: https://cn.bing.com/
如果直接在浏览器中输入 URL,或者直接通过收藏夹来访问页面时是没有 Referer 的.
4.5 Cookie
Cookie 中存储了一个字符串,这个数据可能是客户端(网页)自行通过JS写入的,也可能来自于服务器(服务器在HTTP响应的header中通过Set-Cookie字段给浏览器返回数据)
Cookie 中的字符串也是由键值对组成的
Cookie 的本质, 是浏览器在本地存储 用户自定义数据的一种关键机制:
既然需要存储,那要如何存?
直接存储到硬盘文件上,是不是就可以了? 这样是玩玩不行的!!!
如果允许网页能够操作你电脑的文件系统,那么,一旦你不小心点到了一个奇怪的网站,很可能,网站会做一些恶意的事情, 比如把你硬盘上的学习资料,一锅端了~~
为了保证用户上网比价安全,浏览器会做出限制,进制网页能够直接访问硬盘~~
浏览器虽然禁止了直接访问硬盘,浏览器提供了Cookie 机制,允许网页往浏览器这边存储一些自定义的键值对~~ 这些数据通过浏览器提供的 api, 写入特定的文件中
由于网页有很多,我们访问搜狗,是需要一些数据,访问百度,也需要存一些数据~~
针对这种情况,Cookie 的存储机制是分开的, 每个网站都存自己的 cookie(cookie 是按照域名为维度进行存储的)
同一个网站(搜狗的主页,和搜索的结果页,搜狗图片,搜狗知道...)共享一份cookie.
不同的网站(搜狗和百度)则是各有各自的 cookie~~
Cookie 从哪来?
从服务器来,当我们的浏览器访问服务器的时候,服务器会在 HTTP响应中,通过 Set-Cookie 字段,把Cookie 的键值对,返回浏览器~~
浏览器接收到这个数据,就会在本地存储~~
Cookie 到哪里去?
会在下次请求的时候,把cookie 带给服务器
Cookie 在浏览器这里,只能是暂时 ''缓存'' ,真正要让这个数据发挥作用,还是得由服务器来
Cookie 有啥用?
Cookie 是浏览器本地存储数据的机制
cookie 可以存任何想存的数据(前提是字符串), 由于 cookie 存储的空间有限, 一般也不会用 cookie 存太大的数据. cookie 这里最典型应用就是存储用户的身份信息.
由于客户端有很多,每个客户端的服务可能不一样
因此,服务器就可以通过 cookie 进行区分.
比如客户端在登录的时候,服务器就识别好客户端的角色,把角色信息返回浏览器,在cookie 中保存,后续客户端访问的时候,带着这个 cookie就行了, 此时服务器就直接知道这个客户端是干啥的了
五 认识状态码
• 200 OK 表示访问成功
• 404 Not Found 要访问的资源不存在
• 403 Forbidden 表示访问被拒绝,没有访问权限
• 405 Method Not Allowed 不支持方法
• 500 Internal Server Error 服务器出现内部错误,
• 504 Gateway Timeout 服务器响应超时
• 302 Move temporarily 临时重定向
• 301 Move Permanently 永久重定向
状态码小结(重点):
六 构造 HTTP 请求
6.1 通过 form 表单构造 HTTP 请求
form 的重要参数:
• action: 构造 HTTP 请求的 URL 是什么
• method: 构造 HTTP 请求的 "方法" 是GET 还是 POST (form 只支持 GET和 POST).
input 的重要参数:
• type: 表示输入框的类型. text 表示文本, password 表示密码, submit 表示提交按钮
• name : 表示构造出的 HTTP 请求的 query string 的key. query string 的value 就是输入框的用户输入的内容
• value: input 标签的值. 对于 type 为submit 类型来说, value 就对应了按钮上显示的文本.
注:请求的方法为 GET 时, 数据在首行的query string 中, 请求的方法为 POST 时, 数据在 body中.
6.2 通过 ajax 构造 HTTP 请求
<script> $.ajax({ url: "https://www.sogou.com/", type: "get", //可以写作 post ,put, delete 等等,支持大小写 //响应成功后,会被浏览器自动执行,这个执行过程就是"异步" 的 // 在我们页面 js 中,把请求发出去了就不管了,就继续执行后续代码了.知道说,响应回来了之后, //浏览器就会把这个响应喂给咱们的代码~~(浏览器调用上述的这个 success 回调函数,执行处理响应的逻辑) //也就是说,这个回调函数并不会被立刻调用,只有当响应回来后,才会被调用. data:"这是body", contentType: "text/plain", success: function(body){ //写处理响应的代码,body就是响应的body } }); </script>
相比之下,ajax 的功能比 form 更丰富,更灵活~~
ajax 虽然有许多的优势,但是有一个非常重要的问题:
这是属于 ajax 的一个非常典型的问题:跨域问题
现在运行 ajax 代码的页面的域名: abc.com
但是 ajax 里的请求,访问的域名是: def.com
这俩域名不一致的话,哪怕是服务器给你了响应数据,浏览器还是不能处理,还是要报错.
这个东西是浏览器为了安全引入的保护机制.form则允许跨域.
七 HTTPS
HTTPS 也是一个应用层协议,是在 HTTP协议的基础上引入了一个加密层.
HTTP 协议内容都是按照文本的方式明文传输的. 这就导致在传输过程中出现一些被篡改的情况.
7.1 "加密" 是什么
加密就是把明文(要传输的信息)进行一系列变换,生成密文
解密就是把密文再进行一系列变换,还原成明文
在这个加密和解密的过程中,往往需要一个或者多个中间的数据,辅助进行这个过程,这样的数据成为秘钥
7.2 HTTPS的工作过程
既然要保证数据的安全,就需要进行"加密",
网络传输不在直接传输明文了,而是加密之后的"密文"
加密的方式有很多,但是整体可以分为两大类:对称加密 和 非对称加密
7.3 对称
对称加密就是通过一个"秘钥", 把明文加密成密文,并且也能把密文解密成明文
引入对称加密之后,即使数据被截获,由于黑客不知道秘钥是啥,因此就无法进行解密,也就不知道请求的真实内容是啥了.
但是事情并没有这么简单,服务器同一时刻其实是给很多客户端提供服务的 .这么多的客户端,每个人用的秘钥都必须是不同的(如果相同那秘钥就太容易扩散了,黑客就也能拿到了). 因此 服务器就需要维护每个客户端和每个秘钥之间的关联联系,这也是个很麻烦的事情
比较理想的做法,就是能够在客户端和服务器建立连接的时候,双方确定这次的秘钥是啥.
但是如果直接把秘钥明文传输,那么黑客就能获得秘钥了~~ 此时后续的加密操作就形同虚设了.
因此秘钥的传输也必须机密传输!
但是要想对秘钥进行对称加密,就仍然需要先协商确定一个"秘钥的秘钥".这就形成了"套娃"的问题了.此时秘钥的传输再用对称加密就行不通了.
此时就需要引入了非对称加密
7.4 非对称加密
非对称加密要用到两个秘钥,一个叫做"公钥",一个叫做"私钥".
公钥和私钥是配对的.最大的缺店就是运算速度非常慢,比对称加密要慢的多.
• 通过公钥对明文加密,变成密文
• 通过私钥对密文解密,变成明文
也可以反着用
• 通过私钥对密文加密,变成密文
• 通过公钥对密文解密,变成明文
非对称加密的数学原理比较复杂,涉及到一些数论相关的知识
可以简单的认为公钥是一把锁,谁都可以看见,而私钥就是锁的钥匙.
• 客户端先向服务器发送请求,获取公钥.
• 客户端在本地生成对称秘钥,通过公钥加密,发送给服务器
• 由于中间的网络设备没有私钥(私钥只有服务器有),即使截获了数据,也无法还原出内部的原文,也就无法获取到对称秘钥
• 服务器通过私钥机密,还原出客户端发送的对称秘钥,并且使用这个对称秘钥加密给客户端返回的响应数据
• 后续客户端和服务器的通信都只通过对称加密即可.由于该秘钥只有客户端和服务器两个主机知道,其他主机/设备不知道秘钥即使截获数据也没有意义.由于对称加密的效率比非对称加密高很多,因此只是在开始阶段协商秘钥的时候使用非对称加密,后续的传输仍然使用对称加密.
那么接下来问题又来了:
在服务器返回自己的公钥时,黑客可以截获公钥,把服务器公钥替换成自己的公钥,然后将篡改后的响应数据返回给客户端.
客户端收到响应数据后,通过黑客的公钥把自己的对称秘钥加密发送给服务器,此时黑客再次截获,使用自己的私钥对密文进行解密. 获取对称秘钥后,使用服务器的公钥给对称秘钥加密,返回给服务器.
服务器收到密文后,使用自己的私钥解密,获取到对称秘钥,接下来使用对称秘钥和客户端进行传输数据.
由于黑客获取到了对称秘钥,而服务器和客户端都没有察觉,使用对称秘钥对数据进行加密传输时,由于黑客也有这个对称秘钥,因此数据此时又是不安全的了.
此时,就涉及到了 证书
7.5 证书
在客户端和服务器刚一建立连接的时候,服务器就会给客户端返回一个证书
这个证书就好比人类的身份证,作为这个网站的身份表示,搭建一个HTTPS 网站要在CA机构先申请一个证书(类似于去公安局半个身份证)
这个证书可以理解成是一个结构化的字符串,里面包含了以下信息:
• 证书的发布机构
• 证书的有效期
• 公钥(服务器的公钥)
• 证书的所有者
• 签名(由CA机构自己的私钥加密的一个校验和)
• ......
当客户端获取到这个证书之后, 会对证书进行校验(防止证书是伪造的)
• 判断证书的有效期是否过期
• 判定证书的发布机构是否信任(操作系统中已内置的受信任的证书发布机构,并含有对应机构私钥的公钥,用来解密签名,获取校验和)
• 判断证书是否被篡改: 对签名解密,得到一个校验和,设为sum1. 然后计算整个证书的 校验和sum2. 对比sum1 和sum2 是否相对.如果相等,则说明证书是没有被篡改过的.
黑客是否可以篡改服务器中的公钥?
一旦黑客把证书中的服务器的公钥篡改了,此时客户端计算签名的时候,就发现,数值不对.此时客户端的浏览器就会弹出一个大大的错误弹框,告诉你这个页面存在风险
黑客是否可以把公钥和签名一起改了?
黑客使用自己的公钥,替换证书公钥,同时按新的公钥重新计算签名不就行了?
这也是不行的!!!! 算出来的这个签名,需要使用证书的权威机构的私钥进行加密(黑客没有)
黑客自己整个私钥加密??更不行,因为此时客户端要拿权威机构的公钥机密~~
无法得到校验和时仍会弹出弹框.
总结
HTTPS工作过程中涉及到的秘钥有三组
第一组(非对称加密): 用于校验证书是否被篡改,服务器持有私钥(私钥在注册证书时获得),客户端持有公钥(操作系统包含了可信任的CA 认证机构有哪些,同时持有对应的公钥).服务器使用这个私钥对证书的签名进行加密.客户端通过这个公钥解密获取到证书的签名,从而校验证书内容是否是篡改过.
第二种(非对称加密): 用于协商生成对称加密的秘钥,服务器生成这组 私钥-公钥 对,然后通过证书把公钥传递给客户端,然后客户端用这个公钥给生成的对称加密的秘钥加密,传输给服务器,服务器通过私钥解密获取到对称加密秘钥.
第三组(对称加密): 客户端和服务器后续传输的数据都通过这个对称秘钥机密解密.