文章目录
- 1. 了解 HTTP 协议
- 1.1 HTTP 是什么
- 1.2 抓包工具的使用
- 1. Fiddler 的下载
- 2. Fiddler 基本的设置
- 3. Fillder 的使用
- 1.3 观察一个抓包结果
- 2. HTTP 协议的报文格式
- 2.1 请求报文格式
- 2.2 响应报文格式
- 2.3 报文格式的注意事项
- 3. 认识 URL
- 3.1 URL 基本格式
- 3.2 分析一个具体的 URL:
- 3.3 URL 中的可省略部分
- 3.4 关于 URL encode 和 URL decode
- 4. HTTP 请求(Request)
- 4.1 HTTP 方法
- 4.1 GET 方法
- ① 构造 HTTP GET 请求的情况
- ② 使用 Fiddler 观察 GET请求
- 4.2 POST 方法
- ① 构造 HTTP POST 请求的情况
- ② 使用 Fiddler 观察 POST 请求
- 4.3 面试题: 谈谈 GET 和 POST 的区别
- 4.4 其他相关方法
- 4.2 认识请求报头 (header)
- ① Host
- ② Content-Length
- ③ Content-Type
- 1) application/x-www-form-urlencoded
- 2) multipart/form-data:
- 3) application/json
- ④ User-Agent
- ⑤ Referer
- ⑥ cookie
- 4.3 认识请求正文 (body)
- ① application/x-www-form-urlencoded
- ② multipart/form-data
- ③ application/json
- 5. HTTP 响应(Response)
- 5.1 认识状态码 (status code)
- ① 200 OK
- ② 404 Not Found
- ③ 403 Forbidden
- ④ 500 Internal Server Error
- ⑤ 302 Move temporarily
- 5.2 认识响应正文 (body)
- ① text/html
- ② text/css
- ③ application/javascript
- ④ application/json
- 6. 构造 HTTP 请求
- 6.1 通过 form 表单构造 HTTP 请求
- ① 构造 GET 请求
- ② 构造 POST 请求
- 6.2 通过 ajax 构造 HTTP 请求
- ① 发送 GET 请求
- ② 发送 POST 请求
- ③ 通过第三方库来封装 ajax
- 6.3 通过 Java socket 构造 HTTP 请求
- 7. HTTPS
- 7.1 什么是 HTTPS
- 7.2 为什么引入 HTTPS
- 7.3 HTTP 的工作流程
- ① 引入对称加密
- ② 引入非对称加密
- ③ 引入证书
- 7.4 HTTPS 传输过程
1. 了解 HTTP 协议
1.1 HTTP 是什么
HTTP (HyperText Transfer Protocol, 超文本传输协议) 是一种应用非常广泛的 应用层协议.
所谓 “超文本” 的含义, 就是传输的内容不仅仅是文本(比如 html, css 这个就是文本), 还可以是一些
其他的资源, 比如图片, 视频, 音频等二进制的数据
1.2 抓包工具的使用
1. Fiddler 的下载
① 可以直接在官网下载 Fiddler官网地址
② 也可以直接进入 fiddler搜索页
2. Fiddler 基本的设置
① 首先设置 点击 Tools
-> Options
② 点击 HTTPS
将下面能勾选的勾上
3. Fillder 的使用
将左边的内容清空,然后再进入一个网站,找到对应的那个
按照图片的顺序进行点击,右上就是请求,右下就是响应
1.3 观察一个抓包结果
HTTP 请求
HTTP响应
2. HTTP 协议的报文格式
2.1 请求报文格式
首行: [方法]
[URL]
[版本]
Header: 请求的属性.
空行
Body: 空行后面的内容为 Body.
2.2 响应报文格式
首行: [版本号]
[状态码]
[状态码的解释]
Header: 请求的属性.
空行
Body: 空行后面的内容为 Body.
2.3 报文格式的注意事项
-
首行的内容之间有一个空格.
-
请求的属性是使用冒号分割的键值对.
每组属性之间使用n分割
遇到空行表示Header部分结束 -
Body 允许为空.
如果Body存在,Header中会有一个Content-Length属性来标识Body的长度 -
协议格式总结
3. 认识 URL
3.1 URL 基本格式
3.2 分析一个具体的 URL:
https://dict.youdao.com/result?word=1&lang=en
https
: 协议方案名.user:pass
: 登录信息. 目前一般会省略dict.youdao.com
: 服务器地址. 此处是一个 “域名”, 域名会通过 DNS 系统解析成一个具体的 IP 地址端口号
: 目前一般会省略. http协议默认使用 80 端口. https协议默认使用 443 端口/result
带层次的文件路径word=1&lang=en
: 查询字符串(query string). 本质是一个键值对结构,键值对之间使用&
分割,键和值之间使用=
分割片段标识
: 此 URL 中省略了片段标识. 片段标识主要用于页面内跳转.
3.3 URL 中的可省略部分
协议名
: 可以省略, 省略后 默认为 http://ip 地址 / 域名
: 在 HTML 中可以省略(比如 img, link, script, a 标签的 src 或者 href 属性). 省略后表示服务器的 ip / 域名与当前 HTML 所属的 ip / 域名一致.端口号
: 可以省略. 省略后如果是 http 协议, 端口号自动设为 80; 如果是 https 协议, 端口号自动设为 443.带层次的文件路径
: 可以省略. 省略后相当于 / . 有些服务器会在发现 / 路径的时候自动问/index.html查询字符串
: 可以省略片段标识
: 可以省略
3.4 关于 URL encode 和 URL decode
像 / ? : = &
等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现
把特殊字符,转换成转义字符 => URL encode
把转义字符,还原成原来的字符 => URL decode
4. HTTP 请求(Request)
4.1 HTTP 方法
4.1 GET 方法
① 构造 HTTP GET 请求的情况
- 直接在浏览器中输入 URL
- HTML 中的 link,img,a,script 标签等
- form 表单
- ajax
- 使用 java代码/其他的库
- 通过 linux 下的 wget / curl
- 通过第三方工具,postman 这类工具
② 使用 Fiddler 观察 GET请求
在浏览器中输入sogou.com
GET 请求的特点
- 首行的第一部分为 GET
- URL 的 query string 可以为空, 也可以不为空.
- header 部分有若干个键值对结构.
- body 部分为空.(可以不为空)
关于 GET 请求的 URL 长度问题
HTTP 协议由 RFC 2616 标准定义.没有对 URL 的长度有任何的限制
4.2 POST 方法
① 构造 HTTP POST 请求的情况
- form表单
- ajax
- 第三方工具
② 使用 Fiddler 观察 POST 请求
POST 请求的特点
- 首行的第一部分为 POST
- URL 的 query string 一般为空 (也可以不为空)
- header 部分有若干个键值对结构.
- body 部分一般不为空. body 内的数据格式通过 header 中的 Content-Type 指定. body 的长度由header 中的 Content-Length 指定.
4.3 面试题: 谈谈 GET 和 POST 的区别
GET 和 POST 之间没有本质的区别
- 数据位置: GET 把自定义数据放到 query string, POST 把自定义数据放到 body
- 语义区别: GET 一般用于"获取数据",POST 一般用于提交数据
- 幂等性: GET 请求一般会设计成"幂等". POST 请求一般不要求设计成"幂等"(如果多次请求得到的结果一样, 就视为请求是幂等的)
- 可缓存: GET 请求一般会被缓存 POST 请求一般不能被缓存
4.4 其他相关方法
- PUT 与 POST 相似,只是具有幂等特性,一般用于更新
- DELETE 删除服务器指定资源
- OPTIONS 返回服务器所支持的请求方法
- HEAD 类似于GET,只不过响应体不返回,只返回响应头
- TRACE 回显服务器端收到的请求,测试的时候会用到这个
- ConNECT 预留,暂无使用
这些方法都可以使用ajax来构造.(也可以通过第三方工具).
4.2 认识请求报头 (header)
header 的整体的格式也是 “键值对” 结构
每个键值对占一行. 键和值之间使用分号分割
① Host
表示服务器主机的地址和端口
② Content-Length
表示 body 中的数据长度
③ Content-Type
表示 body 中的数据格式的类型
1) application/x-www-form-urlencoded
在 form 表单提交的时候会出现的数据格式类型.
此时的body的格式
2) multipart/form-data:
通常用于提交图片/文件
body的格式
3) application/json
body格式
④ User-Agent
字段User-Agent会将创建请求的浏览器和用户代理名称等信息传达给服务器
这里的 Windows NT 10.0;WOW64
表示操作系统的信息
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
表示浏览器信息
⑤ Referer
表示这个页面是从哪个页面跳转过来的.
如果直接在浏览器中输入URL, 或者直接通过收藏夹访问页面时是没有 Referer 的
⑥ cookie
因为HTTP是无状态的协议,无法根据之前的状态进行本次的请求处理
为了保留无状态协议这个特征,于是引入了 cookie 信息来控制客户端的状态.
cookie会根据从服务器端发送的响应报文内的一个叫做 Set-cookie 的首部字段信息,通知客户端保存 cookie.当下次再给该服务器发送请求的时候,客户端会自动在请求报文中加入cookie值后发送出去.
服务器端发现客户端发送来的 cookie 后,会去检查是哪一个客户端发来的连接请求,对比服务器上的记录,最后得到之前的状态信息.
4.3 认识请求正文 (body)
① application/x-www-form-urlencoded
② multipart/form-data
③ application/json
5. HTTP 响应(Response)
5.1 认识状态码 (status code)
① 200 OK
这是一个最常见的状态码, 表示访问成功.
② 404 Not Found
没有找到资源.
③ 403 Forbidden
表示访问被拒绝. 有的页面通常需要用户具有一定的权限才能访问(登陆后才能访问). 如果用户没有登陆直接访问, 就容易见到 403.
④ 500 Internal Server Error
服务器出现内部错误. 一般是服务器的代码执行过程中遇到了一些特殊情况(服务器异常崩溃)会产生这个
状态码
⑤ 302 Move temporarily
临时重定向.
重定向就和呼叫转移一样,
就是换了个手机号,别人呼叫你旧手机号,会自动转到新手机号上
5.2 认识响应正文 (body)
正文的具体格式取决于 Content-Type.
① text/html
body中的数据格式是 HTML
② text/css
body中的数据格式是css
③ application/javascript
body中的数据格式是javascript
④ application/json
body中的数据格式是json
6. 构造 HTTP 请求
6.1 通过 form 表单构造 HTTP 请求
① 构造 GET 请求
代码:
<form action="http://www.baidu.com" method="GET"> <input type="text" name="user"> <input type="password" name="password"> <input type="submit" value="提交"> </form>
当输入 wwww zzzz 提交之后进行抓包
② 构造 POST 请求
<form action="http://www.baidu.com" method="POST"> <input type="text" name="user"> <input type="password" name="password"> <input type="submit" value="提交"> </form>
同样的输入 wwww zzzzz 进行抓包
6.2 通过 ajax 构造 HTTP 请求
① 发送 GET 请求
<script> // 1. 创建 XMLHttpRequest 对象 let httpRequest = new XMLHttpRequest(); // 2. 默认异步处理响应. 需要挂在处理响应的回调函数. httpRequest.onreadystatechange = function () { // readState 表示当前的状态. // 0: 请求未初始化 // 1: 服务器连接已建立 // 2: 请求已接收 // 3: 请求处理中 // 4: 请求已完成,且响应已就绪 if (httpRequest.readyState == 4) { // status 属性获取 HTTP 响应状态码 console.log(httpRequest.status); // responseText 属性获取 HTTP 响应 body console.log(httpRequest.responseText); } } // 3. 调用 open 方法设置要访问的 url httpRequest.open('GET', 'http://42.192.83.143:8080/AjaxMockServer/info'); // 4. 调用 send 方法发送 http 请求 httpRequest.send(); </script>
② 发送 POST 请求
<script> // 1. 创建 XMLHttpRequest 对象 let httpRequest = new XMLHttpRequest(); // 2. 默认异步处理响应. 需要挂在处理响应的回调函数. httpRequest.onreadystatechange = function () { // readState 表示当前的状态. // 0: 请求未初始化 // 1: 服务器连接已建立 // 2: 请求已接收 // 3: 请求处理中 // 4: 请求已完成,且响应已就绪 if (httpRequest.readyState == 4) { // status 属性获取 HTTP 响应状态码 console.log(httpRequest.status); // responseText 属性获取 HTTP 响应 body console.log(httpRequest.responseText); } } // 3. 调用 open 方法设置要访问的 url httpRequest.open('POST', 'http://42.192.83.143:8080/AjaxMockServer/info'); // 4. 调用 setRequestHeader 设置请求头 httpRequest.setRequestHeader('Content-Type', 'application/x-www-formurlencoded'); // 5. 调用 send 方法发送 http 请求 httpRequest.send('name=zhangsan&age=18'); </script>
③ 通过第三方库来封装 ajax
<script src="https://releases.jquery.com/git/jquery-git.min.js"></script> <script> $.ajax({ type: 'GET', url:'http://www.baidu.com/index.html', success: function(data, status){ // data 是响应body status 是状态码描述 console.log(status); console.log(data); } }) </script>
6.3 通过 Java socket 构造 HTTP 请求
import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;import java.nio.charset.StandardCharsets;public class HttpClient { private Socket socket; private String ip; private int port; public HttpClient(String ip,int port) throws IOException { socket = new Socket(ip,port); this.port = port; this.ip = ip; } public String get(String url) throws IOException { StringBuilder request = new StringBuilder(); // 构造首行 request.append("Get " + url + " HTTP/1.1n"); // 构造 header request.append("Host: " + ip + ":" + port + "n"); // 构造空行 request.append("n"); // GET 不需要 body, 构造完毕 OutputStream outputStream = socket.getOutputStream(); // outputStream 是一个字节流,以字节为单位进行写入,因此需要把 StringBuilfer 转换乘byte[] outputStream.write(request.toString().getBytes()); // 读取响应 InputStream inputStream = socket.getInputStream(); // 1M 大小的缓冲区,用来存放响应数据 byte[] buffer = new byte[1024 * 1024]; // n 表示实际上读到的字节数 int n = inputStream.read(buffer); return new String(buffer,0,n,"utf-8"); } public String post(String url,String body) throws IOException { StringBuilder request = new StringBuilder(); // 构造首行 request.append("POST" + url + "HTTP/1.1n"); // 构造header request.append("Host: " + ip + ":" + port + "n"); request.append("Content-Type: text/plainn"); request.append("Content-Length: " + body.getBytes().length + "n"); // 构造空行 request.append("n"); // 构造 body request.append(body); // 发送请求 OutputStream outputStream = socket.getOutputStream(); outputStream.write(request.toString().getBytes()); // 读取响应 InputStream inputStream = socket.getInputStream(); byte[] buffer = new byte[1024 * 1024]; int n = inputStream.read(buffer); return new String(buffer, 0, n, "utf-8"); } public static void main(String[] args) throws IOException { HttpClient httpClient = new HttpClient("42.192.83.143",9090); String resp = httpClient.get("/AjaxMockServer/info"); System.out.println(resp);// String resp = httpClient.post("/AjaxMockServer/info","这是正文");// System.out.println(resp); }}
7. HTTPS
7.1 什么是 HTTPS
HTTPS 也是一个应用层协议. 是在 HTTP 协议的基础上引入了一个加密层(SSL/TLS).
7.2 为什么引入 HTTPS
因为HTTP是明文传输, 本来要传什么,实际上就传了什么,但是一旦这样传输,在传输的过程中, 被第三方截获到了,就可能造成信息泄露.
于是引入了 HTTPS在HTTP基础上进行了加密,进一步的保护了用户的信息安全.
明文: 真正要传输的信息
密文: 加密之后的消息
加密: 就是把 明文 (要传输的信息)进行一系列变换, 生成 密文 .
解密: 就是把 密文 再进行一系列变换, 还原成 明文 .
在这个加密和解密的过程中, 往往需要一个或者多个中间的数据, 辅助进行这个过程, 这样的数据称为 密钥
7.3 HTTP 的工作流程
① 引入对称加密
对称加密其实就是通过同一个 “密钥” , 把明文加密成密文, 并且也能把密文解密成明文.
例子: 现有 明文: 1111 密钥: 6666 加密解密过程通过: 按位异或
A 将明文: 1111 通过 密钥: 6666 加密成 密文:2 发送给B
B 通过密钥: 6666 对密文: 7773 进行解密 得到 明文: 1111
此时同时也引入了一个问题,当客户端把密钥进行明文传输的时候,也可能被别人截获,再次发送密文,别人就可以通过密钥获取到明文,那此时的加密就没什么作用了
解决办法: 对密钥进行加密传输.
② 引入非对称加密
非对称加密要用到两个密钥, 一个叫做 “公钥”, 一个叫做 “私钥”
公钥和私钥是配对的. 最大的缺点就是运算速度非常慢,比对称加密要慢很多
此时也有一个问题,可能在获取的公钥就是假的.
解决办法: 引入证书.
③ 引入证书
在客户端和服务器刚一建立连接的时候, 服务器给客户端返回一个 证书.
这个证书包含了刚才的公钥, 也包含了网站的身份信息
当客户端获取到这个证书之后, 会对证书进行校验(防止证书是伪造的).
- 判定证书的有效期是否过期
- 判定证书的发布机构是否受信任(操作系统中已内置的受信任的证书发布机构).
- 验证证书是否被篡改: 从系统中拿到该证书发布机构的公钥, 对签名解密, 得到一个 hash 值(称为数据摘要), 设为 hash1. 然后计算整个证书的 hash 值, 设为 hash2. 对比 hash1 和 hash2 是否相等.如果相等, 则说明证书是没有被篡改过的.
7.4 HTTPS 传输过程
- 客户端先从服务器那边获取到证书.证书里包含了公钥.
- 客户端对整数进行校检.
- 客户端生成一个对称密钥,使用公钥对对称密钥进行加密,发送给服务器
- 服务器得到这个请求后,使用私钥解密,得到对称密钥.
- 客户端发出后续的请求,后续的请求都是使用这个对称密钥加密的.收到的数据也都是使用这个对称密钥解密的.