文章目录
请求格式认识URLquery string关于 URL encode
认识 “方法” (method)GET方法POST 方法GET 和 POST 的区别
认识请求 “报头” (header)HostContent-LengthContent-TypeUser-Agent (简称 UA)RefererCookieCookie抓包理解登录过程Cookie小结
认识请求 “正文” (body)⭕总结
请求格式
HTTP 请求报文由请求行、请求头部、空行 和 请求包体 4 个部分组成 (我们这里学习HTTP可以用到博主在上一篇博客介绍的Fiddler抓包工具,获取本机的http请求和响应)
本片文章将从以下四个方面对HTTP请求报文进行解析
URL方法请求报头正文
认识URL
平时我们俗称的 “网址” 其实就是说的 URL (Uniform Resource Locator 统一资源定位符).
互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它.
一个URL的格式如下:
协议方案名: 常见的有 http 和 https, 也有其他的类型.(例如访问 mysql 时用的jdbc:mysql ) 登陆信息: 现在的网站进行身份认证一般不再通过 URL 进行了, 一般都会省略 服务器地址: 此处是一个 “域名”, 域名会通过 DNS 系统解析成一个具体的 IP 地址 端口号: 上面的 URL 中端口号被省略了, 当端口号省略的时候, 浏览器会根据协议类型自动决定使用哪个端口 (例如 http 协议默认使用 80 端口, https 协议默认使用 443 端口) 带层次的文件路径: 标识要访问的服务器的哪一个资源 查询字符串(query string): 具体的表示了请求的资源是什么, 本质是一个键值对结构. 键值对之间使用 & 分隔, 键和值之间使用 = 分隔. 片段标识: 主要用于页面内跳转
我们先抓一个包来看一下URL在包里面的位置
注意:这个请求头是没有请求正文的
URL 中的可省略部分
协议名: 可以省略, 省略后默认为 http:// ip 地址 / 域名: 在 HTML 中可以省略(比如 img, link, script, a 标签的 src 或者 href 属性). 省略后表示服务器的 ip / 域名与当前 HTML 所属的 ip / 域名一致. 端口号: 可以省略. 省略后如果是 http 协议, 端口号自动设为 80; 如果是 https 协议, 端口号自动设为 443. 带层次的文件路径: 可以省略. 省略后相当于 / . 有些服务器会在发现 / 路径的时候自动访问/index.html 查询字符串: 可以省略 片段标识: 可以省略
query string
query string 中的内容是键值对结构. 其中的 key 和 value 的取值和个数, 完全都是程序猿自己约定的. 我们可以通过这样的方式来自定制传输我们需要的信息给服务器
关于 URL encode
像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现. 比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.
一个中文字符由 UTF-8 或者 GBK 这样的编码方式构成, 虽然在 URL 中没有特殊含义, 但是仍然需要进行转义. 否则浏览器可能把 UTF-8/GBK 编码中的某个字节当做 URL 中的特殊符号.
转义的规则如下: 将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式
认识 “方法” (method)
方法种类有以下几种 其中最常用的为GET,POST也挺常用的,其他的很少用到,所以下面着重介绍GET和POST
GET方法
GET 是最常用的 HTTP 方法. 常用于获取服务器上的某个资源.
在浏览器中直接输入 URL, 此时浏览器就会发送出一个 GET 请求.
另外, HTML 中的 link, img, script 等标签, 也会触发 GET 请求
GET 请求的特点
首行的第一部分为 GET URL 的 query string 可以为空, 也可以不为空. header 部分有若干个键值对结构. body 部分为空.
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 也可以有正文, POST 也可以没有正文, 并非绝对
使用习惯上有区别 :
语义不同: GET 一般用于获取数据, POST 一般用于提交数据. GET 的 body 一般为空, 需要传递的数据通过 query string 传递, POST 的 query string 一般为空, 需要传递的数据通过 body 传递 GET 请求一般是幂等的, POST 请求一般是不幂等的. (如果多次请求得到的结果一样, 就视为请求是幂等的) GET 可以被缓存, POST 不能被缓存. (这一点也是承接幂等性).
认识请求 “报头” (header)
header 的整体的格式也是 “键值对” 结构.
每个键值对占一行. 键和值之间使用分号分割.
报头的种类有很多, 此处仅介绍几个常见的
Host
标识最终要访问的服务器的地址(和端口)
在 URL中虽然已经写入了域名, 通常情况下这两个是一致的, 但如果不是直接访问, 而是通过代理访问服务器, 这二者就不同了
Content-Length
表示 body 中的数据长度,单位是字节
如果是 GET 方法获取的请求, 一般没有 Body, 所以就不显示这个键值对了
Content-Type
表示请求的 body 中的数据格式
常见选项:
application/x-www-form-urlencoded: form 表单提交的数据格式. 此时 body 的格式形如:
title=test&content=hello
multipart/form-data: form 表单提交的数据格式(在 form 标签中加上enctyped=“multipart/form-data” . 通常用于提交图片/文件. body 格式形如:
Content-Type:multipart/form-data; boundary=----
WebKitFormBoundaryrGKCBY7qhFd3TrwA
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="text"
title
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png
PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
application/json: 数据为 json 格式. body 格式形如
{"username":"123456789","password":"xxxx","code":"jw7l","uuid":"d110a05ccde64b16a861fa2bddfdcd15"}
这里博主只介绍了几种常见的选项
User-Agent (简称 UA)
表示浏览器/操作系统的属性. 形如
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/91.0.4472.77 Safari/537.36
其中 Windows NT 10.0; Win64; x64 表示操作系统信息
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36 表示浏览器信息.
User-Agent 的设计是一个历史遗留问题, 如今的作用大不如前, 不过至少可以用来区分用户是使用电脑/手机/平板
Referer
表示这个页面是从哪个页面跳转过来的.
如果直接在浏览器中输入URL, 或者直接通过收藏夹访问页面时是没有 Referer 的.
Cookie
Cookie 中存储了一个字符串, 这个数据可能是客户端(网页)自行通过 JS 写入的, 也可能来自于服务器(服务器在 HTTP 响应的 header 中通过 Set-Cookie 字段给浏览器返回数据).
往往可以通过这个字段实现 “身份标识” 的功能
Cookie抓包
我们可以通过抓包观察页面登陆的过程(以gitee码云为例):
清除之前的 cookie 为了方便观察, 先清除掉之前登陆的 cookie 在码云页面上, 点击 url 左侧的图标, 选择 Cookie 然后移除已经存在的 Cookie 登陆操作 然后我们使用fidder进行抓包,抓登录请求包和登录响应包
登陆请求:
POST https://gitee.com/login HTTP/1.1
Host: gitee.com
Connection: keep-alive
Content-Length: 39
Cache-Control: max-age=0
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
Origin: https://gitee.com
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/w
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://gitee.com/login
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
encrypt_key=password&utf8=%E2%9C%93&authenticity_token=36ZqO9tglSN6EB6pF6f2Gt%2B
在首次的登录请求响应包里面,我们是找不到Cookie,因为这时候服务器开没有给Cookie
登录响应:
HTTP/1.1 302 Found
Date: Thu, 10 Jun 2021 04:15:58 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Keep-Alive: timeout=60
Server: nginx
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-UA-Compatible: chrome=1
Expires: Sun, 1 Jan 2000 01:00:00 GMT
Pragma: must-revalidate, no-cache, private
Location: https://gitee.com/HGtz2222
Cache-Control: no-cache
Set-Cookie: oschina_new_user=false; path=/; expires=Mon, 10 Jun 2041 04:16:00
-0000
Set-Cookie: gitee_user=true; path=/
Set-Cookie: gitee-sessionn=M1Rhbk1QUUxQdWk1VEZVQ1BvZXYybG13ZUJFNGR1V0pSYTZyTllEa21pVHlBUE5QU2Qwdk44NXdEam
11T3FZYXFidGNYaFJxcTVDRE1xU05GUXN0ek1Uc08reXRTay9ueTV3OGl5bTdzVGJjU1lUbTJ4bTUvN1
l3RFl4N2hNQmI1SEZpdmVJWStETlJrdWtyU0lDckhvSGJHc3NEZDFWdHc5cjdHaGVtNThNcEVOeFZlaH
c0WVY5NGUzWjc2cjdOcCtSdVJ0VndzdVNxb3dHK1hPSDBDSFB6WlZDc3prUVZ2RVJyTnpTb1c4aFg1Mm
UxM1YvQTFkb1EwaU4zT3hJcmRrS3dxVFZJNXoxaVJwa1liMlplbWR5QXQxY0lvUDNic1hxN2o0WDg1Wk
E9LS10N0VIYXg4Vm5xdllHVzdxa0VlUEp3PT0%3D-
-2f6a24f8d33929fe88ed19d4dea495fbb40ebed6; domain=.gitee.com; path=/; HttpOnly
X-Request-Id: 77f12d095edc98fab27d040a861f63b1
X-Runtime: 0.166621
Content-Length: 92
You are being redirected.GET https://gitee.com/HGtz2222 HTTP/1.1
Host: gitee.com
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
可以看到, 响应中包含了 3 个 Set-Cookie 属性.
其中我们重点关注第三个. 里面包含了一个 gitee-session-n 这样的属性, 属性值是一串很长的加密之后的信息. 这个信息就是用户当前登陆的身份标识. 也称为 "令牌(token)
访问其他页面 登陆成功之后, 此时可以看到后续访问码云的其他页面(比如个人主页), 请求中就都会带着刚才获取到的Cookie 信息
ET https://gitee.com/HGtz2222 HTTP/1.1
Host: gitee.com
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/91.0.4472.101 Safari/537.36
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,imag
e/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
sec-ch-ua-mobile: ?0
Referer: https://gitee.com/login
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: oschina_new_user=false; user_locale=zh-CN; yp_riddler_id=1ce4a551-a160-
4358-aa73-472762c79dc0; visit-gitee--2021-05-06%2010%3A12%3A24%20%2B0800=1;
sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22726826%22%2C%22first_id%22%3
A%22175869ba5888b6-0ea2311dc53295-303464-2073600-
175869ba5899ac%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E
7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%
9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%2
4latest_referrer%22%3A%22%22%7D%2C%22%24device_id%22%3A%22175869ba5888b6-
0ea2311dc53295-303464-2073600-175869ba5899ac%22%7D; remote_way=svn;
tz=Asia%2FShanghai;
Hm_lvt_24f17767262929947cc3631f99bfd274=1622637014,1622712683,1622863899,1623298
442; Hm_lpvt_24f17767262929947cc3631f99bfd274=1623298550; gitee_user=true;
gitee-sessionn=M1Rhbk1QUUxQdWk1VEZVQ1BvZXYybG13ZUJFNGR1V0pSYTZyTllEa21pVHlBUE5QU2Qwdk44NXdEam
11T3FZYXFidGNYaFJxcTVDRE1xU05GUXN0ek1Uc08reXRTay9ueTV3OGl5bTdzVGJjU1lUbTJ4bTUvN1
l3RFl4N2hNQmI1SEZpdmVJWStETlJrdWtyU0lDckhvSGJHc3NEZDFWdHc5cjdHaGVtNThNcEVOeFZlaH
c0WVY5NGUzWjc2cjdOcCtSdVJ0VndzdVNxb3dHK1hPSDBDSFB6WlZDc3prUVZ2RVJyTnpTb1c4aFg1Mm
UxM1YvQTFkb1EwaU4zT3hJcmRrS3dxVFZJNXoxaVJwa1liMlplbWR5QXQxY0lvUDNic1hxN2o0WDg1Wk
E9LS10N0VIYXg4Vm5xdllHVzdxa0VlUEp3PT0%3D-
-2f6a24f8d33929fe88ed19d4dea495fbb40ebed6
我们可以看到此时你的请求中Cookie 字段也包含了一个 gitee-session-n 属性, 里面的值和刚才服务器返回的值相同. 后续只要访问 gitee 这个网站, 就会一直带着这个令牌, 直到令牌过期/下次重新登陆
理解登录过程
我们再来理解一下登陆过程,如下图所示:
Cookie小结
Cookie 从哪里来?
Cookie (中存储的数据)从服务器来: 第一次登陆一个网站的时候, 服务器会返回一个响应, 在这个响应报文中, 通过 Set-Cookie 把需要存储的值返回给客户端, 客户端进行存储
在该响应报文里面包含了一个 gitee-session-n 这样的属性, 属性值是一串很长的加密之后的信息. 这个信息就是用户当前登陆的身份标识. 也称为 “令牌(token)”
Cookie 到哪里去?
Cookie 要回到服务器, 当客户端收到 Set-Cookie 中的数据后, 后续每次请求网页的时候, 给服务器发送的请求报文中都有一个 Cookie 字段, 相当于把 Cookie 里面的值再带给服务器
后续请求你中的 Cookie 字段也包含了一个 gitee-session-n 属性, 里面的值和刚才服务器返回的值相同, 后续只要访问 gitee 这个网站, 就会一直带着这个令牌, 直到令牌过期(销毁)/下次重新登陆
Cookie 有什么用?
Cookie 是一浏览器客户端在本地存储用户自定义数据的一种机制, 比如存储用户的身份信息, 让服务器通过 Cookie 来识别用户身份
认识请求 “正文” (body)
正文中的内容格式和 header 中的 Content-Type 密切相关. 上面也罗列了三种常见的情况
这里就不再做过多赘述了,大家也可以自行通过抓包进行观察。
⭕总结
感谢大家的阅读,希望得到大家的批评指正,和大家一起进步,与君共勉!
推荐链接
发表评论