HTTP/HTTPS 协议,是应用层协议。
1. HTTP
HTTP (全称为"超文本传输协议")是⼀种应用非常广泛的应用层协议。
HTTP 是一问一答模式的协议,客户端发一个请求,服务器就返回一个响应,请求和响应一一对应。
- 在网络通信中,除了 一问一答的模型,也有其他:
- 一问多答:下载大文件(将要下载的大文件拆分成多个子文件下载)
- 多问一答:上传大文件(将大文件拆分成多个子文件进行上传)
- 多问多答:远程控制(例如 todeak 这个软件)
浏览器打开网页的场景,或者手机app 加载数据的场景,就是典型的一问一答的场景,使用 http就非常合适。
我们平时打开⼀个网站,就是通过 HTTP 协议来传输数据的,例如,当我们在浏览器中输入⼀个搜狗搜索的"网址"(URL)时, 浏览器就给搜狗的服务器发送了⼀个 HTTP 请求,搜狗的服务器返回了⼀个 HTTP 响应。
这个响应结果被浏览器解析之后,就展示成我们看到的页面内容。(这个过程中浏览器可能会给服务器发送多个HTTP请求,服务器会对应返回多个响应,这些响应里就包含了页面HTML,CSS,JavaScript,图片,字体等信息)。
- 所谓 "超文本" 的含义,就是传输的内容不仅仅是文本(比如 html, css 这个就是文本),还可以是⼀些其他的资源,比如图片, 视频, 音频等二进制的数据。
1)理解应用层协议
我们已经学过 TCP/IP , 已经知道目前数据能从客户端进程经过路径选择跨网络传送到服务器端进程 [ IP+Port ]。
可是,仅仅把数据从A点传送到B点就完了吗?
- 这就好比,在淘宝上买了⼀部手机,卖家[客户端] 把手机通过顺丰[传送+路径选择] 送到买家[服务 器] 手里就完了吗?
- 当然不是,买家还要使用这款产品,还要在使用之后,给卖家打分评论。
所以,我们把数据从A端传送到B端, TCP/IP 解决的是顺丰的功能,而两端还要对数据进行加工处理或者使用,所以我们还需要⼀层协议,不关心通信细节,关心应用细节! 这层协议叫做应用层协议。而应用是有不同的场景的,所以应用层协议是有不同种类的,其中经典协议之⼀就有HTTP协议。
- 再回到我们刚刚说的买手机的例子,顺丰相当于 TCP/IP 的功能,那么买回来的手机都附带了说明书【产品介绍,使用介绍,注意事项等】,而该说明书指导用户该如何使用手机,此时的说明书可以理解为用户层协议
2)理解HTTP协议的工作过程
当我们在浏览器中输入⼀个 "网址",此时浏览器就会给对应的服务器发送⼀个HTTP 请求。对方服务器收到这个请求之后,经过计算处理,就会返回⼀个 HTTP响应。
事实上, 当我们访问⼀个网站的时候, 可能涉及不止⼀次的 HTTP 请求/响应 的交互过程,可能还需要图片、视频、CSS、JS等的HTTP请求/响应,因为我们访问的网站可能就需要包含这些图片等。
注意: 当前 搜狗主页 是通过 https 来进行通信的。https 是在 http 基础之上做了⼀个加密解密的工作作,后⾯再介绍。
3)HTTP协议格式
关于 HTTP 报文格式,需要搭配一个重要的抓包工具 Fiddler进行学习。抓包工具能够获取到网络的数据包,然后通过详细的格式解析出来。
浏览器访问 sogou.com 时,就会把 HTTP 请求先发给 Fiddler,Fiddler 再把请求转发给 sogou 的服务器。当 sogou 服务器返回数据时,Fiddler 拿到返回数据,再把数据交给浏览器. 因此Fiddler 对于浏览器和 sogou 服务器之间交互的数据细节,都是非常清楚的。
理解抓包工具:
- 抓包工具,相当于代理,分为正向代理(代表客户端干活)和反向代理(代表服务器干活)
- 示例:
抓包工具是一个软件,电脑上的所有网络通信,都会先发给这个抓包程序,抓包程序再把数据转发给服务器。
- wireshark 抓包工具,可以抓很多协议,例如HTTP、TCP、UDP、IP、以太网数据帧等
- Fiddler 抓包工具,是专门抓 HTTP的,功能更简单,使用起来也更简单,这里我们使用这个工具
打开 Fiddler ,在浏览器上随便打开一个网页,例如打开 搜狗,可以看到,它抓到了 HTTP协议:
当前的请求和响应的列表
在这多多行中,找到以下这一条蓝色的记录:它是一次HTTP/HTTPS 请求的核心记录
- 红色表示报错;蓝色表示这个请求得到了一个网页;绿色表示得到了一个js;灰色表示这个响应的数据已经被缓存了。
双击打开这条记录:右边的界面就显示出了请求和响应的数据:
- 左侧列表:所有被捕获的请求
- 右侧上半部分:Request(请求数据)
- 右侧下半部分:Response(响应数据)
选择以下的选项,可以显示请求的原始数据:
点击右下角的按钮,可以将这段请求数据以 文本 的形式打开:
关于 响应,也是一样的操作,先让其显示原始的数据:
通过记事本打开:
响应数据,经常是压缩后传输的,是为了节省网络带宽。
可以点击这个黄色的框,让响应恢复压缩之前:
再次通过记事本打开:
通过 Fiddler,可以做到很多事情,爬虫本质上就是HTTP的客户端,自己写代码实现,伪造出差不多的请求,就可以达成类似的功能效果。
总结
- 左侧窗口显示了所有的 HTTP请求/响应,可以选中某个请求查看详情。
- 右侧上方显示了HTTP请求的报文内容(切换到 Raw 标签页可以看到详细的数据格式)
- 右侧下方显示了HTTP响应的报文内容(切换到 Raw 标签页可以看到详细的数据格式)
- 请求和响应的详细数据,可以通过右下角的 View in Notepad 通过记事本打开。
4)HTTP报文格式学习
接下来,通过 Fiddler 捕获到的HTTP协议,我们来学习HTTP的格式。
请求 格式:
- 首行(请求行):[⽅法] + [url] + [版本],首先是请求方法 —— GET,然后是 URL——https://www.sougou.com/,接着是版本号 —— HTTP/1.1。这一行直接告诉服务器“我要做什么、对谁做、用什么规则做”。
- 接着是 请求头部(header),提供请求的元信息,控制服务器的行为,携带客户端的能力与上下文。
- 最后是空行,就是单独的回车换行,用来标识请求头结束。
其实请求的格式中,还有第4部分——正文(body),即请求体,不过有些请求没有正文(就像上述的请求),有些请求有正文。
示例:一个有正文的请求,打开某一个网页:
响应 格式:
- 首行(状态行):[版本号] + [状态码] + [状态码解释],告诉客户端:“我按照什么协议版本处理了,结果如何。”
- 响应头部(header),格式和请求头一样:字段名: 字段值,携带关于服务器、关于响应体、关于后续行为的元信息。
- 空行,标志响应头的结束
- 正文(body),即响应体,这是服务器返回的实际数据。可以是一段 HTML、一张图片、一段 JSON、一个 PDF 文件等。
总结:
问题: 为什么 HTTP 报文中要存在 "空行"?
- 因为 HTTP 协议并没有规定报头部分的键值对有多少个。空行就相当于是 "报头的结束标记",或者是 "报头和正文之间的分隔符"。
- HTTP在传输层依赖 TCP 协议,TCP 是面向字节流的,如果没有这个空行,就会出现"粘包问题"(形象的比喻:数据像“粘”在了一起,让接收方分不清每条消息的边界在哪里)
5)HTTP 请求(request)
【1】URL
平时我们俗称的 "网址" 其实就是说的 URL (Uniform Resource Locator 统⼀资源定位符)。互联网上的每个文件都有⼀个唯⼀的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
URL基本格式:
- 关于登录信息,这个现在一般不会写在URL里面,而是单独有一个登录的界面
- 关于片段标识符,是区分当前这个页面的哪个部分的,现在也有,但不是很多,一般会出现在文档类的网站上
例如,之前学习 JDBC时,创建DataSource,就需要设置 URL,又比如在搜狗搜索框中搜索 "计算机" ,它的网址格式:
用生活中的例子来比喻URL:
- 我去六食堂的麻辣烫档口吃麻辣烫,档口号是10号,它的汤底有 番茄汤底、金汤汤底、麻辣汤底
- 那么此时根据我的选择,URL为:https://六食堂:10/麻辣烫/番茄汤底?辣=微辣&醋=不要放
关于片段标识符,例如打开 vue 网页:
点击如图的目录,URL的片段标识符如图:借助片段标识符实现路径的效果
点击另一个目录,就不一样了:
如何知道域名对应的IP地址呢?
- 可以使用ping 域名查看域名对应的IP地址,打开cmd:
关于 query string:query string 中的内容是键值对结构。其中的 key 和 value 的取值和个数,完全都是程序员自己约定的,我们可以通过这样的方式来自定制传输我们需要的信息给服务器。
URL 中的可省略部分
- 协议名: 可以省略,省略后默认为 http://,不过现代浏览器从你输入的那一刻起就在尽力把你推向 HTTPS,但如果你的网站只支持 HTTP,它最终还是会用 HTTP 打开。
- ip 地址 / 域名: 在 HTML 中可以省略(比如 img, link, script, a 标签的 src 或者 href 属性)。省略后表示服务器的 ip / 域名与当前 HTML 所属的 ip / 域名⼀致。
- 端⼝号: 可以省略,省略后如果是 http 协议, 端口号自动设为 80;如果是 https 协议, 端口号⾃动设为 443。
- 带层次的文件路径: 可以省略,省略后相当于/.有些服务器会在发现 / 路径的时候自动访问/index.html。
- 查询字符串: 可以省略。
- 片段标识: 可以省略。
关于 URL encode:
观察以下的一个URL:
我们发现,当我们想要搜索关于 C++ 的内容时,query的值变成了 C%2B%2B,这是什么原因?
这是因为 URL 中本身就有一些特殊符号,代表不同的特殊含义,例如,: ,/ ,? ,# ,& ,= 等,
而 query string 的内容是程序员自定义的,就有可能包含到上述的URL的特殊符号,这些符号被url当做特殊意义理解了,因此这些字符不能随意出现,如果某个参数中需要带有这些特殊字符,就必须先对特殊字符进行转义。
转义规则:将需要转码的字符转为16进制,每2位做⼀位,前面加上%,编码成%XY格式。
- 像上述的例子中,包含有特殊字符 +,就需要转义。通过查询 ASCII码表,将 + 转义为十六进制,即为 2B,再加上%,那么 C++,就是 C%2B%2B
- 需要进行转义操作的,不仅仅是标点符号,对于中文等其他非ASCII码(非英语)系的文字,也是需要转义的,因为 URL 协议本身只接受 ASCII 字符。只不过很多浏览器为了用户看起开方便,显示的是转义之前的,实际上抓包中就能看到,是已经转义的数据。一般是查询 UTF-8 码表来查询16进制。
而 URL decode 就是 URL encode的逆过程,把转义的内容还原。
【2】方法(method)
方法的作用就是,知道这次的请求是要干什么的。前面打开搜狗网页的抓包的请求中就是一个 GET方法,它的作用就是从服务器中获取某个资源(搜狗网页)。
这些方法中,最常用的就是 GET和POST,其次是PUT和DELETE,其他的几乎不怎么用。
- 获取 html ,获取 css,获取 js 等操作,都是 get
- 想要抓包获取到多的 css(紫色)和js(绿色),就使用ctrl+F5操作,该操作是强制刷新
- 通常情况下,从网络加载数据比从硬盘上加载要慢,浏览器为了加快访问页面的速度,就会把页面依赖的一些静态资源( css, js, 图片,字体,mp3等)这些内容缓存到硬盘上,第一次访问服务器,需要加载这么多东西,后续再访问,就不必重新加载,而强制刷新操作,就会忽略本地缓存,所有资源都重新从服务器获取。
- 而 登录,上传文件,是典型的 post
GET 请求一般都是没有 正文body 的,如果需要通过 GET 给服务器发送一些数据,是通过
query string 传递后过去的。
例如,我想要更换 gitee 的头像,使用 GET 的话就需要把换头像的需求写在 query string 上,当使用浏览器打开这个 URL后,浏览器就会直接执行修改头像的操作,也就是说,这个 URL 被谁看到,谁就能换头像,非常不安全。
而对于POST来说,它的请求是带有 正文body的,正文中就保存了当前上传的数据内容,就像把数据装进信封,亲手交给服务器,而不是像 GET 那样直接将数据公开展示。
- 就是自己 "手动更换",示例,打开 https://gitee.com/zzzz 这是一个 GET 请求,只拿页面)
页面里有一个表单,你选择本地图片文件,点击“上传”。
浏览器发出一个 POST 请求:更换头像请求的命令不在URL里,且图片数据在请求体里:图片数据是二进制数据(通过特殊的方法进行转码),这个请求就无法用一个链接就触发。
使用 Fiddler 观察 POST 请求:
在码云的登陆页面, 输入用户名,密码,验证码之后,点击登陆, 就可以看到 POST 请求.
更换码云的头像,看到 POST 请求:
可以看到更换头像的内容在正文里:
使用 Fiddler 观察 GET 请求:
打开浏览器,访问 搜狗主页,可以看到 GET 请求:
可以看到是没有正文的:
总结:
GET 是最常用的 HTTP 方法,常用于获取服务器上的某个资源。在浏览器中直接输入 URL, 此时浏览器就会发送出⼀个 GET 请求。
GET 是“打开一个 URL 就执行操作”→ 不适合任何有副作用的操作。
GET 的特点:
首行的第⼀部分为 GET
URL 的 query string 可以为空, 也可以不为空
header 部分有若干个键值对结构
body 部分为空。
POST 方法也是⼀种常见的方法。多用于提交用户输入的数据给服务器(例如登陆页面)
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 不能被缓存.(这⼀点也是承接幂等性)
其他方法:
- PUT 与 POST 相似,只是具有幂等特性,⼀般用于更新
- DELETE 删除服务器指定资源
- OPTIONS 返回服务器所支持的请求方法
- HEAD 类似于GET,只不过响应体不返回,只返回响应头
- TRACE 回显服务器端收到的请求,测试的时候会用到这个
- CONNECT 预留,暂⽆使用
实现 Restful 风格(设计服务器的一种"习惯")的 api 的时候,会使用到 PUT和DELETE
- 新增:POST
- 删除:DELETE
- 修改:PUT
- 查询:GET
实际上,这四个操作中的任何一个都可以实现增删查改,完全取决于代码怎么写。
【3】请求报头(header)
header 的整体的格式也是 "键值对" 结构,每个键值对占⼀行,键和值之间使用分号分割。
1] Host
表示服务器主机的地址和端口。
HTTP 协议中,传输的时候可能会涉及到 "加密"(HTTPS),而 URL部分是不会被加密的,被加密的是 header 和 body,服务器收到请求之后,也可以做一个最终校验,验证URL中的内容和header中加密的内容是否一致。
2] Content-Length
Contenet-Length 表示 body 中的数据长度。
HTTP协议,在传输层这里是是基于 TCP 实现的(版本号<=2.0),所谓的HTTP协议,就是把字符串构造成 HTTP 约定的格式,即:
- 首行
- 请求头
- 空行
- 正文
把上述的这一串字符串,写入到 tcp socket 中,对于 TCP来说,一个连接上可以发多个请求,服务器收到数据后就得区分一下,从哪里到哪里是一个完整的http 请求数据:
- 没有 body 的请求,读到空行,就可以认为是结束了
- 有 body 的请求,先读取首行和 header,读到空行,解析到 header 中的Content-Length,根据这个值,接下来再读取固定的字节的长度。
示例:以下是一个有正文的 POST 请求
以上的 application/x-www-form-urlencoded: form 是表单提交的数据格式。
3] Content-Type
Content-Type 表示请求的 body 的数据格式。
提示了接收方如何解析 body 中的数据。HTTP这里能够携带的东西比较多:
- HTML :Content-Type 是text/html,浏览器会解析其中的标签,把标签转化成界面显示
- CSS:Content-Type 是text/css,浏览器会解析其中的选择器和属性,并且把这里指定的内容应用到页面的样式上。
- JS :Content-Type 是application/javascript,浏览器会通过 js 引擎解释执行 js 中的逻辑。
- JSON:Content-Type 是application/javascript,浏览器不会做任何处理(由对应的js程序员写的逻辑中)
- 图片:Content-Type 是image/png 或者 image/jpg 等,浏览器尝试按照图片的二进制格式解析出来并显示。
示例:通过 ctrl+F5 强制刷新:
请求和响应都会用到这 Content-Length 和 Content-Type ,如果都有 body,并没有这两个属性或者只有一个,都会认为是非法的/错误的 http 报文。
4] User-Agent(简称UA)
表示浏览器/操作系统的属性,User-Agent 里面表示了用户使用的浏览器和操作系统的情况。
- Windows NT 10.0; Win64; x64 表示操作系统信息
- AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 表示浏览器信息
在同一时间段内,有的用户的浏览器版本比较旧,支持的功能少,有些用户的浏览器版本比较新,支持的功能也多,可以根据用户使用的设备,对可使用的功能进行区分,通过 UA 中的浏览器版本/操作系统版本,区分出当前用户的设备最多都支持哪些功能,然后返回功能少/多的页面给用户
5] Referer
描述当前页面的来源,即这个页面从哪个页面跳转过来的。
(注意:直接输入 url 或者点收藏栏打开的页面是没有 Referer的。)
示例:打开搜狗搜索引擎:输入框中搜索 "计算机":抓包可以看到
它的 Referer 就是 搜狗的 URL,即表示 这个"计算机" 的页面是从搜狗搜索页面跳转过来的:
6] Cookie
浏览器展示页面的过程中,页面里虽然可以通过 js 来实现一些逻辑,但是 js 代码无法访问用户的硬盘(文件系统),但是,有时候还是希望把某些数据能够保存到本地硬盘中的,因此,浏览器引入了 Cookie 机制,cookie 就是浏览器允许网页在本地硬盘存数据的一种机制,不是让网页代码直接访问文件系统,而是做了一层抽象,浏览器的 cookie 提供了键值对存储机制。
在浏览器中可以直接看到当前页面保存的 Cookie 有哪些:
调式当前页面 F12:
如这个页面的所有 Cookie 键值对:可以看出 cookie 是按照域名维度来组织的
而以上的 cookie 键值对都是程序员自定义的。
浏览器保存了这些 cookie 之后,就会在后续给服务器发送请求的时候,把这些 cookie 键值对放到请求 cookie header中传输给服务器。
- Cookie 到哪里去:最终发回给服务器
- Cookie 从哪里来:也是从服务器中来
Cookie 中存储了⼀个字符串, 这个数据可能是客户端(网页)自行通过 JS 写入的, 也可能来自于服务器 (服务器在 HTTP 响应的 header 中通过 Set-Cookie 字段给浏览器返回数据)。
往往可以通过这个字段实现 "身份标识" 的功能。
可以通过抓包观察页面登陆的过程(以码云为例):
【为了方便观察, 先清除掉之前登陆的 cookie 】
登录请求:
登陆响应:
可以看到, 响应中包含了 3 个Set-Cookie属性。
其中我们重点关注第三个,里面包含了⼀个gitee-session-n这样的属性, 属性值是⼀串很长的加密之后的信息.这个信息就是用户当前登陆的身份标识. 也称为 "令牌(token)" 。
登陆成功之后, 此时可以看到后续访问码云的其他页面(比如个人主页), 请求中就都会带着刚才获取到的 Cookie 信息:
请求你中的 Cookie 字段也包含了⼀个 gitee-session-n 属性, 里面的值和刚才服务器返回的值相同. 后续只要访问 gitee 这个网站, 就会⼀直带着这个令牌, 直到令牌过期/下次重新登陆。
【Cookie 是可能会过期的,服务器返回 Cookie 的时候,是可以设置有效时间的,如果Cookie 中的 sessionId 过期了,此时就需要用户重新登陆】
理解登录过程:
总结:Cookie 是浏览器在本地存储数据的一种方式(键值对),而这个Cookie 是服务器通过 Set-Cookie 返回,后续给服务器发的请求,都会带上 Cookie。
【4】请求正文(body)
关于正文,前面已经做了详细的介绍,这里不再展开。
6)HTTP 响应
1] 状态码
状态码表示访问⼀个页面的结果 (是访问成功, 还是失败, 还是其他的⼀些情况)。
关于状态码全分类 (1xx/ 2xx /3xx /4xx /5xx):https://developer.aliyun.com/article/1722126
下面理解几个常见的状态码。
200 OK
表示请求完全成功,服务器已返回请求的目标资源。
示例:打开搜狗搜索页,我们通过抓包软件 Fiddler 观察:可以看到它右侧的列表中,表示请求返回成功。
404 Not Found
常用的客户端错误,表示访问的资源没有找到。URL无效或资源已删除/不存在。
示例:访问 https://www.sogou.com/123.html 这个不存在的页面:
通过抓包观察:
可以通过在 body 中返回一些 html 之类的,把 404 页面做一些装饰:
示例:b站就对 404 的页面做了一个美化,例如,打开 b站 一个不存在的网页:https://www.bilibili.com/123.html
403 Forbidden
表示访问被拒绝,没有权限。
示例:查看码云的私有仓库, 如果不登陆, 就会出现 403.
405 Method Not Allowed
请求的URI存在,但不支持客户端使用的HTTP方法。
前面我们已经学习了 HTTP 中所支持的方法, 有 GET, POST, PUT, DELETE 等. 但是对方的服务器不⼀定都支持所有的方法(或者不允许用户使用⼀些其他的方法)。
500 Internal Server Error
服务器出现错误,服务器处理逻辑的代码中抛出异常,但是没有 catch 到。
504 Gateway Timeout
作为网关/代理的服务器,等待上游源服务器响应超时,即超时未收到响应。
在服务器资源紧张的时候容易触发。
302 Found
临时重定向状态码,表示请求的资源临时被移到了另一个地址(由响应头 Location 指定),即访问服务器A,服务器A 告诉你去访问服务器 B。
资源临时移动到新URI,客户端本次使用新地址,后续仍使用原地址
典型场景:未登录用户 访问需要登录的页面,临时跳转到登录页
301 Moved Permanently
永久重定向状态码,资源已永久移动到新URI,后续所有请求应使用新地址。
典型场景:网站域名永久变更(如 oldsite.com → newsite.com)
- 想要迁移域名,如果直接迁移,就会使得所有保存旧域名的用户无法访问,把服务器架设在新域名上,给旧域名设置重定向,可以重定向到新域名,此时用户访问新域名或者旧域名(自动跳转到新域名)都可以。
总结:
2] 响应报头(header)
内容和请求报头 一样,这里不再赘述。
3] 响应正文(body)
正⽂的具体格式取决于 Content-Type
- text/html
- text/css
- application/javascript
- application/json
7)构造 HTTP 请求
①通过 form 表单构造 HTTP 请求
form (表单) 是 HTML 中的⼀个常用标签. 可以用于给服务器发送 GET 或者 POST 请求。
示例1:form 发送 GET 请求
<form action="http://abcdef.com/myPath" method="GET"> <input type="text" name="userId"> <input type="text" name="classId"> <input type="submit" value="提交"> </form>通过浏览器打开,在输入框中输入值,然后点击提交:
此时就会构造出一个 HTTP 请求并发送出去。
构造的HTTP请求:
注意:这里的服务器的IP是瞎写,只是为了展示过程。
form 代码和 HTTP 请求之间的对应关系:
- form 的 action 属性对应 HTTP 请求的 URL
- form 的 method 属性对应 HTTP 请求的方法
- input 的 name 属性对应 query string 的 key
- input 的 内容 对应 query string 的 value
示例2:form 发送 POST 请求
修改上⾯的代码, 把 form 的 method 修改为 POST :
<form action="http://abcdef.com/myPath" method="POST"> <input type="text" name="userId"> <input type="text" name="classId"> <input type="submit" value="提交"> </form>页面效果不变:
构造的 HTTP 请求:
与 GET 的主要区别:
- method 从 GET 变成了 POST
- 数据从 query string 移动到了 body 中.
②通过 ajax 构造 HTTP 请求
ajax 全称 Asynchronous Javascript And XML, 是 2005 年提出的⼀种 JavaScript 给服务器发送 HTTP 请求的方式. 特点是可以不需要 刷新页面/页面跳转 就能进行数据传输。
在 JavaScript 中可以通过 ajax 的方式构造 HTTP 请求。但是 ajax 的原生 api 比较难用,XMLHttpRequest 类,后来有了第三方库 jQuery 等。
③通过 Java socket 构造 HTTP 请求
HTTP 请求,本质上就是 TCP 请求,只需要构造字符串,符合 HTTP 协议的格式,写入到 TCP socket 中即可。
④通过 Postman 工具构造 HTTP 请求
在该工具中,可以选择请求以什么方法发送:
示例:向搜狗服务器发送请求:
能够看到它的默认 header,也可以进行添加:
还可以选择是否添加 body,以及 body 的格式:
点击 send 后,就会给我们返回结果:
如下图,点击右上角的图标,还可以将左侧的内容生成你想要的语言的代码:
除了使用 Postman 以外,还可以使用 apifox 。
2. HTTPS
HTTPS 也是⼀个应用层协议,是在 HTTP 协议的基础上引入了⼀个加密层,即SSL/TLS 加密协议(也是应用层协议,专门用来加密)。
HTTP 协议内容都是按照文本的方式明文传输的,这就导致在传输过程中出现⼀些被篡改的情况
在互联网上, 明文传输是比较危险的事情。HTTPS 就是在 HTTP 的基础上进行了加密, 通过密文来传输,进⼀步的来保证用户的信息安全。
1)加密
加密就是把明文(要传输的信息)进行⼀系列变换,生成密文。
解密就是把密文再进行⼀系列变换,还原成明文。
在这个加密和解密的过程中, 往往需要⼀个或者多个中间的数据, 辅助进行这个过程, 这样的数据称为密钥。(密钥可以认为是一个很长的字符串)
2)HTTPS 的工作过程
既然要保证数据安全, 就需要进行 "加密"。
网络传输中不再直接传输明文了,而是加密之后的 "密文"。
加密的方式有很多, 但是整体可以分成两大类: 对称加密 和 非对称加密。
如果是按照 明文传输,那么当有黑客入侵路由器时,就很容易能够获取到传输的数据内容,也容易进行篡改。
那么此时就引入对称加密,对数据的内容进行加密,解决数据内容的安全性问题。
①对称加密
对称加密其实就是通过同⼀个 "密钥" , 即对称密钥,把明文加密成密文, 并且也能把密文解密成明文。
如以下图,引入对称加密之后, 即使数据被截获, 由于黑客不知道密钥是啥, 因此就无法进行解密, 也就不知道请求的真实内容是啥了,就更不必说篡改。
但是,服务器在同一时刻,其实要给N个客户端提供服务,这么多个客户端,每个客户端的密钥必须是不同的,就像银行卡的密码,每个人都是不同的,因此服务器就需要维护每个客户端和每个密钥之间的关联关系,这是个很麻烦的事情。
因此,就需要在客户端与服务器建立连接的时候,双方协商确定这次的密钥是什么,需要一方生成唯一的密钥,再通过网络传输给另一方。
假设是客户端生成密钥:
那么问题又来了,此时的密钥是明文传输的,那么黑客就能轻易的获取到密钥,此时后续的加密操作就形同虚设了。
因此,密钥的传输也必须是加密传输。
但是要想对密钥进行对称加密, 就仍然需要先协商确定⼀个 "密钥的密钥",即假设密钥是key,对 key 使用 key2 密钥进行加密,那么此时 key 就可以密文传输给服务器,但是 此时 key2 它是明文传输的,再使用一个 key3 密钥……这就开始套娃了。
这说明,密钥,使用对称加密进行加密的方式是行不通的。
这时候就需要引入非对称密钥,通过非对称密钥对密钥加密,解决密钥传输的安全性问题。
非对称加密就是加密使用一个密钥,解密也使用一个密钥,不再是都使用同一个密钥加密解密。
②非对称密钥
非对称加密要用到两个密钥,⼀个叫做 "公钥", ⼀个叫做 "私钥"。(其中一个密钥是服务器公开出去的,就是公钥,另一个自己保存好,就是私钥)
公钥和私钥是配对的,最大的缺点就是运算速度非常慢,比对称加密要慢很多。
- 可以是公钥对明文进行加密,变成密文,再通过私钥对密文解密,变成明文(公钥加密,私钥解密)。
- 也可以是私钥对明文进行加密,变成密文,再通过公钥对密文解密,变成明文(私钥加密,公钥解密)。
- 客户端在本地生成对称密钥, 通过公钥加密, 发送给服务器
- 由于中间的网络设备(黑客)没有私钥, 即使截获了数据, 也无法还原出内部的原⽂, 也就无法获取到对称密钥
- 服务器通过私钥解密, 还原出客户端发送的对称密钥. 并且使用这个对称密钥加密给客户端返回的响应数据
- 后续客户端和服务器的通信都只用对称加密即可. 由于该密钥只有客户端和服务器两个主机知道, 其他主机/设备不知道密钥即使截获数据也没有意义
- 由于对称加密的效率比非对称加密高很多, 因此只是在开始阶段协商密钥的时候使用非对称加密, 后续 的传输仍然使用对称加密
总结:当有黑客入侵了客户端的路由器,此时客户端决定对称密钥是666,然后通过公钥加密传输给服务器,服务器通过私钥将客户端传输过来的那个对称密钥解密,然后返回响应给客户端说设置成功,由于黑客不知道私钥是什么,无法获取到客户端的对称密钥,而客户端发现对称密钥设置成功后,后续客户端与服务器通信都会使用这个对称密钥。
- 公钥就相当于🔒,而私钥就相当于🔑,这样的🔒,任何人都能从服务器这里领到一把,但是每把锁的🔑只有服务器有。
- 非对称加密阶段:客户端 把一张写有“密码是666”的纸条(对称密钥),通过锁头(公钥加密)装进一个只有 服务器 能打开的小盒子(密文)里,经过路由器寄出。黑客看到这个小盒子,也能看到锁,但打不开,也看不到里面的纸条。这一步是安全的。
- 对称加密阶段:服务器收到小盒子,用钥匙(私钥解密)打开,知道了密码是666。之后,客户端和服务器都用这个666密码(对称密钥),来锁上/打开这个小盒子来装每次的交易数据。
- 而黑客能看到所有你们后续往来的小盒子(密文)。但他不知道密码666,他不能从这个小盒子本身猜出密码是666,即开不了锁。
以上场景中有三个密钥:
- 客户端生成的对称密钥
- 服务器生成的公钥,可以给所有设备使用
- 服务器生成的私钥,只有自己知道
————————
但是,即便像上述如此,还是存在重大的安全隐患的:客户端如何获取到公钥,如何确定公钥不是黑客伪造的呢?
黑客是可以通过中间人攻击来获取到对称密钥,从而破坏后续的传输安全。
- 即 客户端向服务器申请一个锁(公钥)时,黑客和服务器都能看到客户端的请求,此时服务器根据客户端的请求生成一个锁pub1 ,并配了钥匙pri1(私钥) ,将锁pub1 返回给客户端,但是被黑客劫持了,黑客自己做了一把锁pub2 和配对的钥匙pri2 ,然后换成自己的锁pub2 给客户端,而客户端并不知道这些,他就会把 对称密钥key 放在一个小盒子中(密文),通过 "服务器" 返回的锁pub2 进行加密
- 黑客劫持了小盒子,用自己配的钥匙pri2 打开了小盒子(对密文进行解密),获取到了对称密钥,然后再将自己的锁pub2 从小盒子上拿下来,换成之前拦截下来的服务器的 锁pub1 ,重新用锁pub1 上锁之后(重新对密文加密),传输给服务器,服务器拿到小盒子后,使用 钥匙pri1 对小盒子解锁(对密文解密),完全可以打开
- 至此,后续客户端和服务器之间的通信,都会通过这个 对称密钥key 加密传输,而这一切尽在黑客的掌握之中,黑客可以进行窃听甚至修改数据。
————
要解决上述的问题,需要引入校验机制,中间人攻击的关键,在于客户端无法区分收到的公钥是否是服务器真实的公钥,还是黑客篡改的,因此,需要对公钥是否正确进行校验,这时候就需要引入证书。
③证书
服务端在使用HTTPS前,需要向CA机构申领⼀份数字证书,数字证书里含有证书申请者信息、公钥信息等,这个证书可理解为一个结构化的字符串:
- 服务器的公钥
- 服务器的身份信息(如域名 www.bank.com)
- 证书的颁布机构是谁
- 证书的有效期
- CA 数字签名
服务器把证书传输给浏览器,浏览器从证书里获取公钥就行了,无需从服务器那里获取,证书就如身份证,证明服务端公钥的权威性。
数字签名
数字签名就是验证身份的。
数字签名本质上是一个被加密的校验和,这个校验和就是将要校验的数据(例如上述的服务器公钥,身份信息等)带入一个固定的公式而算出来的一个数字,然后再对这个校验和加密,公证机构会生成一对非对称密钥对这个校验码加密。
输入的值相同时,得到的校验和就是相同的,输入的值不同时,得到的校验和大概率是不同的。
关于这个证书,服务器在搭建的时候申请一次就可以了。
客户端收到证书后,就要进行校验:
1️⃣客户端需要针对证书中的其他字段,使用相同的算法,再算一次校验和,得到校验和1(针对客户端收到的数据进行计算的)
- 证书的颁布机构是谁
- 证书的有效期是什么时候
- 服务器的公钥
- 服务器的拥有者(域名)
2️⃣再通过公证机构的公钥,对数字签名进行解密,得到校验和2(针对服务器申请证书时候得到的原始校验和)
3️⃣对比校验和1 和 校验和2 是否相同,如果相同,说明证书没有被修改过,如果不同,证书无效,中间被人篡改
——
那么,如何保证公钥就是公证机构的公钥,而不是黑客伪造的呢?
很简单,公证机构的公钥就不是通过网络传输的,而是操作系统中内置的,安装好系统,系统就内置了一系列知名公证机构的公钥,根本不需要通过网络下载。
如果黑客想要直接修改证书中的公钥为自己的公钥,此时就会导致客户端计算的校验和和解密出来的校验和对不上,此时客户端就会报错,浏览器就会弹出一个红色的页面,告诉你该网站存在风险,是否继续访问。
那黑客能否自己申请一个证书,用自己的证书整个替换服务器的证书?
答案是不能的,证书中包含服务器的域名,黑客自己申请的证书的域名和正经服务器的证书域名肯定是不同的,浏览器这边还是可以验证,输入的URL的域名和得到的证书的域名是不是匹配的,不匹配,同样认为证书非法,还是会弹出上述说的页面。
3)总结
HTTPS 工作过程中涉及到的密钥有三组
第一组(非对称加密):用于校验证书是否被篡改,服务器持有私钥(私钥在注册证书时获得),客户端持有 公钥(操作系统包含了可信任的 CA 认证机构有哪些,同时持有对应的公钥)。服务器使用这个私钥对证书 的签名进行加密。客户端通过这个公钥解密获取到证书的签名,从而校验证书内容是否是篡改过。
第二组(非对称加密):用于协商生成对称加密的密钥,服务器生成这组 私钥-公钥 对,然后通过证书把公钥传递给客户端,然后客户端用这个公钥给生成的对称加密的密钥加密,传输给服务器,服务器通过私钥 解密获取到对称加密密钥。
第三组(对称加密):客户端和服务器后续传输的数据都通过这个对称密钥加密解密.
其实⼀切的关键都是围绕这个对称加密的密钥. 其他的机制都是辅助这个密钥工作的.
- 第二组非对称加密的密钥是为了让客户端把这个对称密钥传给服务器
- 第⼀组非对称加密的密钥是为了让客户端拿到第二组非对称加密的公钥.
————
上述HTTPS的工作流程 :
- 引入对称加密
- 引入非对称加密
- 中间人攻击
- 引入证书 & 数字签名
是 SSL的握手流程(发送非业务的数据-握手),不只是局限于 HTTPS的,其他的基于SSL的网络协议,也是类似的流程。