4. http协议及报文头部结构
浏览器访问网页的过程:
http协议:http/0.9, http/1.0, http/1.1, http/2.0,http/3.0
http协议:stateless 无状态, 服务器无法持续追踪访问者来源
解决http协议无状态方法
- cookie 客户端存放
- session 服务端存放
http事务:一次访问的过程
- 请求:request
- 响应:response
HTTP报文结构
协议查看或分析的工具:tcpdump, wireshark,tshark
参考资料:https://developer.mozilla.org/zh-CN/docs/Web/HTTP
4.1 请求报文
请求报文:
request报文格式:
<method> <request-URL> <version>
<headers>
<entity-body>
- 开始行:
方法
URL
版本 回车换行 - 首部字段:
字段名:值
; 比如: Server:nginx. 不同的浏览器也会自行添加不同的首部字段, 比如浏览器版本 - 首部字段和实体之间有一个空行. 分离首部字段和实体.
- 实体: 存放具体信息, 如果只是访问网站页面, 一般没有实体, 因为URL放在了开始行; 如果是上传文件, 图片, 提交用户名密码等, 那么就会放在实体中.
范例:
GET / HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: www.apache.com
User-Agent: HTTPie/0.9.4
范例: form表单验证POST请求的请求报文
# post.html
<form action="index.html" method="POST">
username:<br>
<input type="text" name="username" >
<br>
password:<br>
<input type="text" name="password" >
<br><br>
<input type="submit" value="Submit">
</form>
开启一个虚拟机 10.0.0.86, 搭建一个apache
[00:16:56 root@CentOS-8-6 ~]#yum -y install httpd; systemctl enable --now httpd
[00:17:58 root@CentOS-8-6 ~]#cd /var/www/html
[00:18:10 root@CentOS-8-6 /var/www/html]#vim post.html
# post.html
<form action="index.html" method="POST">
username:<br>
<input type="text" name="username" >
<br>
password:<br>
<input type="text" name="password" >
<br><br>
<input type="submit" value="Submit">
</form>
访问10.0.0.86/post.html
第一次访问, 发送的是GET请求, 没有请求实体
填写表单, 提交数据, 发送POST请求, 会携带请求实体, 也就是用户名和密码信息
范例: telnet验证请求报文格式
[00:01:28 root@client ~]#telnet 10.0.0.84 80
Trying 10.0.0.84...
Connected to 10.0.0.84.
Escape character is '^]'.
GET / HTTP/1.1 # 开始行: 请求方法, URL, 协议版本, 然后敲Enter
Host: 1.1.1.1 # 首部字段: Host: 1.1.1.1, 然后敲Enter
# 首部字段和实体间的空行, 敲回车. 因为GET请求没有实体, 所以要再敲一次回车, 然后返回结果
HTTP/1.1 302 Found
Date: Sun, 11 Dec 2022 13:01:41 GMT
Server: Apache/2.4.37 (centos) OpenSSL/1.1.1c
Strict-Transport-Security: max-age=31536000
Location: https://1.1.1.1/
Content-Length: 200
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="https://1.1.1.1/">here</a>.</p>
</body></html>
Connection closed by foreign host.
生产案例:
可以利用httpd或者nginx的访问日志中的URL字段, 来统计网站哪个页面, 哪个链接访问的次数最多.
4.2 响应报文
响应报文:
response报文格式:
<version> <status> <reason-phrase>
<headers>
<entity-body>
范例:
HTTP/1.1 200 OK
Cache-Control: max-age=3, must-revalidate
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=UTF-8
Date: Thu, 07 Nov 2019 03:44:14 GMT
Server: Tengine
Transfer-Encoding: chunked
Vary: Accept-Encoding
Vary: Accept-Encoding, Cookie
- 开始行:
HTTP协议版本
状态码
(返回信息的数字, 存放程序执行退出后的状态信息)短语
(解释状态码) - 首部字段:
字段:值
- 空行
- 实体行: 实体大部分情况就是请求的网页文件
4.3 HTTP报文格式详解
4.3.1 Method 方法
请求方法,标明客户端希望服务器对资源执行的动作,包括以下:
- GET: 从服务器获取一个资源
- HEAD: 只从服务器获取文档的响应首部, 如
curl -i
. curl命令的-v
选项会显示详细的请求和响应报文, 其中就
包含请求方法 - POST: 向服务器输入数据,通常会再由网关程序继续处理
- PUT: 将请求的主体部分存储在服务器中,如上传文件
- DELETE: 请求删除服务器上指定的文档
- TRACE:追踪请求到达服务器中间经过的代理服务器
- OPTIONS:请求服务器返回对指定资源支持使用的请求方法
- CONNECT:建立一个到由目标资源标识的服务器的隧道
- PATCH:用于对资源应用部分修改
4.3.2 version版本
HTTP/<major>.<minor>
范例:
HTTP/1.1
4.3.3 status 状态码
http响应码:
参考资料:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
http协议状态码分类
1xx: 100-101 信息提示
2xx: 200-206 成功
3xx: 300-307 重定向
4xx: 400-415 错误类信息, 客户端错误
5xx: 500-505 错误类信息, 服务器端错误
http协议常用的状态码
200: 成功, 请求数据通过响应报文的entity-body部分发送; OK
301: 请求的URL指向的资源已经被删除; 但在响应报文中通过首部**Location**指明了资源现在所处的新位置, 301 Moved Permanently
302: 响应报文Location指明资源临时新位置, 302 Moved Temporarily
304: 客户端发出了条件式请求, 但是服务器上的资源未曾发生改变, 则通过此响应状态码通知客户端; 304 Not Modified. 通过浏览器缓存得到的资源, 比如, 用户访问一个页面时, 发现本地已经有了该资源了, 就没有必须去访问服务器上的资源了
307: 浏览器内部重定向
401: 需要输入账号和密码认证才能访问资源; 401 Unauthorized表示输入的用户名或密码错误
403: 请求被禁止; Forbidden
404: 服务器无法找到客户端请求的资源; 404 Not Found. 属于客户端的错误, 因为访问的资源不存在
500: 服务器内部错误; Internal Server Error
502: 代理(调度)服务器从后端服务器收到了一条伪相应, 如无法连接到网关, 也就是后端服务器出现了问题; 502 Bad Gateway
503: 服务不可用, 临时服务器维护或者过载, 服务器无法处理请求
504: 网关超时; 后端服务器在一定时间内无法相应, 不一定是无法连接, 但是因为到了超时时间后还没连上, 导致连接超时; 504 Gateway Time-out
4.3.4 reason-phrase原因短语
状态码所标记的状态的简要描述
4.3.5 headers首部字段头
首部字段包含的信息最为丰富。首部字段同时存在于请求和响应报文内,并涵盖 HTTP 报文相关的内容信息。使用首部字段是为了给客服端和服务器端提供报文主体大小、所使用的语言、认证信息等内容
首部字段是由首部字段名和字段值构成的,中间用冒号":”分隔字段值对应,即key/value 键/值对
单个 HTTP 首部字段可以有多个值
参考资料:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers
首部的分类:
- 通用首部:请求报文和响应报文两方都会使用的首部
- 请求首部:从客户端向服务器端发送请求报文时使用的首部。补充了请求的附加内容、客户端信息、请求内容相关优先级等信息
- 响应首部:从服务器端向客户端返回响应报文时使用的首部。补充了响应的附加内容,也会要求客户端附加额外的内容信息
- 实体首部:针对请求报文和响应报文的实体部分使用的首部。补充了资源内容更新时间等与实体有关的的信息
- 扩展首部
通用首部:
- Date: 报文的创建时间
- Connection:连接状态,如keep-alive, close
- Via:显示报文经过的中间节点(代理,网关)
- Cache-Control:控制缓存,如缓存时长
- MIME-Version:发送端使用的MIME版本
- Warning:错误通知
请求首部:
Accept:通知服务器自己可接受的媒体类型
Accept-Charset: 客户端可接受的字符集
Accept-Encoding:客户端可接受编码格式,如gzip
Accept-Language:客户端可接受的语言
Client-IP: 请求的客户端IP
Host: 请求的服务器名称和端口号
Referer:跳转至当前URI的前一个URL
-
User-Agent:客户端代理,浏览器版本
条件式请求首部:
- Expect:允许客户端列出某请求所要求的服务器行为
- If-Modified-Since:自从指定的时间之后,请求的资源是否发生过修改
- If-Unmodified-Since:与上面相反
- If-None-Match:本地缓存中存储的文档的ETag标签是否与服务器文档的Etag不匹配
- If-Match:与上面相反
安全请求首部:
- Authorization:向服务器发送认证信息,如账号和密码
- Cookie: 客户端向服务器发送cookie
代理请求首部:
Proxy-Authorization: 向代理服务器认证
响应首部:
信息性:
- Age:从最初创建开始,响应持续时长
- Server:服务器程序软件名称和版本
协商首部:某资源有多种表示方法时使用
- Accept-Ranges:服务器可接受的请求范围类型
- Vary:服务器查看的其它首部列表
安全响应首部:
- Set-Cookie:向客户端设置cookie
- WWW-Authenticate:来自服务器对客户端的质询列表
实体首部:
- Allow: 列出对此资源实体可使用的请求方法
- Location:告诉客户端真正的实体位于何处
- Content-Encoding:对主体执行的编码
- Content-Language:理解主体时最适合的语言
- Content-Length: 主体的长度
- Content-Location: 实体真正所处位置
- Content-Type:主体的对象类型,如text
缓存相关: - ETag:实体的扩展标签
- Expires:实体的过期时间
- Last-Modified:最后一次修改的时间
4.3.6 entity-body实体
请求时附加的数据或响应时附加的数据,例如:登录网站时的用户名和密码,博客的上传文章,论坛上
的发言等。
4.4. Cookie和Session
无状态协议是指协议对事物处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它应答就很快。
HTTP是超本文传输协议,顾名思义,这个协议支持超文本的传输。什么是超文本?说白了就是使用HTML编写的页面。通常,我们使用客户端浏览器访问服务器的资源,最常见的URL也是以html为后缀的文件,因此可以说超文本是网络上最主要的资源。
既然HTTP协议的目的是在于支持超文本的传输,也就是资源的传输,那么客户端浏览器向HTTP服务器发送请求,继而HTTP服务器将相信资源发回给客户端这样一个过程中,无论对于客户端还是服务器,都没有必要记录这个过程,因为每一次请求和响应都是相对独立的,一般而言,一个URL对应着一个唯一的超文本,正是因为这样的唯一性,使得记录用户的行为状态变得毫无意义,所以,HTTP协议被设计为无状态的连接协议符合它本身的需求。
HTTP协议这种特性有优点也有缺点,优点在于解放了服务器,每一次请求"点到为止",不会造成不必要的连接占用,缺点在于如果为了保留状态,每次请求都会传输大量的重复信息内容。
可是随着 Web 的不断发展,很多业务都需要对通信状态进行保存.
如果是一次性会话的过程: 打开浏览器 → 访问一些服务器内容 → 关闭浏览器
但目前有很多WEB访问场景,并不是一次性会话,而是多次相关的会话,比如:
登录场景:打开浏览器 → 浏览到登陆页面 → 输入用户名和密码 → 访问到用户主页(显示用户名) → 修改密码(输入原密码)→ 修改收货地址...
问题:在此处登录会话过程中产生的数据(用户会话数据)如何保存下来呢?
购物场景:打开浏览器 → 浏览商品列表 → 加入购物车(把商品信息保存下来) → 关闭浏览器
打开浏览器 → 直接进入购物车 → 查看到上次加入购物车的商品 → 下订单 → 支付
问题: 在购物会话过程中,如何保存商品信息?
以上场景都需要保留会话数据,需要会话管理机制。
会话管理: 管理浏览器客户端和服务器端之间会话过程中产生的会话数据。
为了会话管理,HTTP就需要传输大量重复信息内容的问题,造成大量的网络带宽消耗。于是 Cookie 和 Session 技术闪亮登场了,它们可以为用户进行会话管理,实现保存状态。
HTTP是无状态的协议, 无法记录用户访问的信息, 需要用cookie和session解决
cookie和session的使用是为了记录用户的信息, 为用户提供专门的服务.
4.4.1 Cookie
Cookie 又称为"小甜饼”。类型为"小型文本文件”,指某些网站为了辨别用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)。由网景公司的前雇员卢·蒙特利在1993年3月发明
因为HTTP协议是无状态的,即服务器不知道用户上一次做了什么,这严重阻碍了交互式Web应用程序的实现。在典型的网上购物场景中,用户浏览了几个页面,买了一盒饼干和两瓶饮料。最后结帐时,由于HTTP的无状态性,不通过额外的手段,服务器并不知道用户到底买了什么,所以Cookie就j是用来绕开HTTP的无状态性的"额外手段”之一。服务器可以设置或读取Cookies中包含信息,借此维护用户跟服务器会话中的状态。
在上面的购物场景中,当用户选购了第一项商品,服务器在向用户发送网页的同时,还发送了一段Cookie,记录着那项商品的信息。当用户访问另一个页面,浏览器会把Cookie发送给服务器,于是服务器知道他之前选购了什么。用户继续选购饮料,服务器就在原来那段Cookie里追加新的商品信息。结帐时,服务器读取发送来的Cookie就行了。
Cookie基于HTTP协议,也叫Web Cookie或浏览器Cookie,是服务器发送到用户浏览器并保存在客户端本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie使基于无状态的HTTP协议记录稳定的状态信息成为了可能。
cookie就是一段小数据(包含键值对和其他辅助信息), 在用户访问服务器时, 服务器端会自动生成一个cookie信息, 通过响应报文首部字段(Set-Cookie), 发回给客户端. 用户下次再访问相同的网站时, 即使中间隔离一段时间, 客户端会把从服务器端收到的cookie信息, 通过请求报文, 首部字段中的Cookie字段(Cookie)发回给服务器端. 服务器端会比对自己原先给用户生成的cookie和用户这次访问时携带的cookie来确认用户身份. 这样就实现了http协议的有状态化, 否则http协议是无状态不记录用户信息的.
Cookie主要用于以下三个方面:
- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为跟踪(如跟踪分析用户行为等)
使用 Cookie 的状态管理
- Cookie 技术通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态。当服务器收到HTTP请求时,服务器可以在响应头里面添加一个Set-Cookie选项。浏览器收到响应后通常会保存下Cookie,之后对该服务器每一次请求中都通过Cookie请求头部将Cookie信息发送给服务器。服务器端发现客户端发送过来的 Cookie 后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息.另外,Cookie的过期时间、域、路径、有效期、适用站点都可以根据需要来指定。
cookie在服务端生成, 在服务器本地缓存一份, 同时发送一份给客户端. 客户端请求时会携带cookie信息, 服务器用接收到的cookie信息和服务器本地缓存的cookie进行比对认证
cookie的问题: 如果把用户相关的信息, 都存放在cookie里, 那么每次用户访问网站都要携带cookie信息, 会造成网络带宽消耗; 另外cookie是有有效期的. cookie的有效期可以在服务器端设定, 并且用户也可以在前端界面选择多久自动登录, 也就是把cookie在服务端保存多久
Set-Cookie首部字段
- NAME=VALUE 赋予 Cookie 的名称和其值,此为必需项
- expires=DATE Cookie 的有效期,若不明确指定则默认为浏览器关闭前为止
cookie分类:
-
会话级cookie: 浏览器不关闭时cookie有效, 浏览器关闭了, cookie就失效了.
基于内存保存,会话期Cookie是最简单的Cookie:浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效。会话期Cookie不需要指定过期时间(Expires)或者有效期(Max-Age)。需要注意的是,有些浏览器提供了会话恢复功能,这种情况下即使关闭了浏览器,会话期Cookie也会被保留下来,就好像浏览器从来没有关闭一样。
-
持久性cookie: 即使关了浏览器, cookie也可以在定义的时间内持续保存.
基于硬盘保存,和关闭浏览器便失效的会话期Cookie不同,持久性Cookie可以指定一个特定的过期时间(Expires)或有效期(Max-Age)。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
提示:当Cookie的过期时间被设定时,设定的日期和时间只与客户端相关,而不是服务端。
-
path=PATH 指定了主机下的哪些路径可以接受Cookie(该URL路径必须存在于请求URL中)。若不指定则默认为文档所在的文件目录,以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。
例如,设置 Path=/docs ,则以下地址都会匹配:
- /docs
- /docs/Web/
- /docs/Web/HTTP
-
domain=域名 指定了哪些主机可以接受Cookie。如果不指定,默认为当前文档的主机(不包含子域名)。如果指定了Domain,则一般包含子域名。
例如,如果设置 Domain=apache.com,则Cookie也包含子域名(如:study.apache.com)
Secure 标记为 Secure 的Cookie只应通过被HTTPS协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过Cookie传输,因为Cookie有其固有的不安全性,Secure 标记也无法提供确实的安全保障。从 Chrome 52 和 Firefox 52 开始,不安全的站点(http:)无法使用Cookie的 Secure 标记。
HttpOnly 加以限制使 Cookie 不能被 JavaScript 脚本访问,为避免跨域脚本 (XSS) 攻击,通过JavaScript的 Document.cookie API无法访问带有 HttpOnly 标记的Cookie,它们只应该发送给服务端。如果包含服务端 Session 信息的 Cookie 不想被客户端 JavaScript 脚本调用,那么就应该为其设置 HttpOnly 标记。
-
浏览器对cookie的限制:
Cookie 存储的限制是不一样的。例如:单个域名可存储的 Cookie 数量、Cookie 大小等。
在进行页面 Cookie 操作的时候,应该尽量保证 Cookie 的个数小于 20 个,总大小小于 4KB,这是一个安全且保险的范围。
网站想要实现cookie, 需要在网站开发层面实现
范例: 浏览器查看存储的cookie信息
范例: 查看保存在本地的cookie信息
Internet选项 → Internet属性 → 常规 → 浏览历史记录(设置) → 当前位置 → 查看文件
由于安全原因, cookie文件已经无法直接在Windows通过文本编辑器打开, 只能通过InternetCookie APIs去获取
范例:响应报文中的set-cookie首部
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
范例:请求报文中的cookie首部字段
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
范例:响应报文set-cookie中的Secure 和 HttpOnly
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
范例:浏览器开发者工具查看cookie
范例: 浏览器禁止cookie
范例:php语言实现cookie的管理
#设置cookie
cat setcookie.php
<?php
setcookie('title','cto'); #有效期为会话级
setcookie('user','wang',time()+3600*12); #有效期为12小时, time()表示当前系统时间, 3600秒是一个小时, +12表示有效期为12小时. 这个有效期对浏览器存储的cookie和服务器存储的cookie都有效
echo "<h1>test setcookie </h1>"
?>
#说明:setcookie设置的cookie,只有下一次http请求才能生效
#显示cookie
cat showcookies.php
<?php
echo "<h1>test showcookie </h1>";
echo $_COOKIE["user"]; #显示user的这一个cookie
echo "<br />";
var_dump($_COOKIE); #显示所有cookie
//print_r($_COOKIE); #不如上面方式详细
?>
#删除cookie,方式1: cookie会在到期后自动被删除
setcookie('user','wang',time()+3600*12)
#删除cookie, 方式2: 手动调整某个cookie的时间, 然后重新执行php程序
#vim delcookie.php
<?php
setcookie('user','wang',time()-3600*12);
echo "<h1>cookie:user is deleted </h1>";
?>
- 安装php
[21:29:42 root@CentOS-8-6 ~]#yum -y install php
- 用php创建cookie
# 该php程序仅用来生成cookie, 并不会在前端显示任何信息
[21:33:13 root@CentOS-8-6 /var/www/html]#vim setcookie.php
<?php
setcookie('title','cto');
setcookie('user','wang',time()+3600*12);
?>
# 一定要重启apache服务
[21:40:27 root@CentOS-8-6 /var/www/html]#systemctl restart httpd
[21:38:48 root@CentOS-8-6 /var/www/html]#ss -ntl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 128 *:80 *:*
- 访问页面
首次访问:
请求报文不会携带cookie
响应报文会在响应首部添加Set-cookie字段, 把php程序产生的cookie发送给浏览器
下次只要访问相同的网站, 无论访问的是哪个页面, 只要访问的页面包含对cookie的处理(增删改查), 都会在请求报文自动携带这个cookie
- 请求报文会携带首次访问时, 从服务端接收到的cookie
- 服务端也会继续发送本地存储的cookie
在edge浏览器查看浏览器端存储的cookie, 因为chrome没有显示cookie具体信息
因为title是会话级cookie, 所以只要把浏览器窗口关闭, title的cookie就会被删除
4.4.2 Session
为了解决cookie携带数据造成网络带宽消耗的问题, 引入了session技术, 当用户访问网站时, 服务器会在内存中给用户生成session信息, 比如session id 和 用户信息. 通过session id就可以定位到一个用户. 这个session信息会以Set-Cookie的方式再返还给用户. 用户再次访问时, 会把这个session信息添加到请求报文的首部字段, 一起发送给服务器. 浏览器通过每次访问时携带session信息, 就可以表明自己的身份. session id只包含了很少的信息,比如包含session id, 那么服务器就可以根据session id来判断用户身份. 不会存放过多数据, 解决了带宽. 基于session是瘦cookie, 而基于cookie形式是胖cookie.
问题: 如果黑客盗取了用户的session id那么就可以冒充用户来登录网站了. 因为http协议本身无状态, 就是依靠cookie和session id来判断用户身份的.
解决方案: session id是一个巨大的数据, 很难计算出来. 并且网站都是基于https会对session id进行加密传输.
session是相对于cookie的另外一个状态保持的解决方案,它是通过服务器来保持状态的。session指的是服务器上为每个客户端所开辟的独立存储空间,在其中保存的信息就是用于保存状态的。
Session是服务器端程序运行的过程中创建的,不同语言实现的应用程序有不同创建session的方法。在创建了session的同时,服务器会为该session生成唯一的sessionId,而这个sessionId被创建了之后,就可以调用session相关的方法往session中增加内容了,而这些内容只会保存在服务器中,每个sessionid就像数据库中主键,可以根据SessionId 关联每个session的相关信息,比如:购物车里的商器,登录用户等。但发送给客户端浏览器的只有sessionId。当客户端浏览器再次发送http请求时,会自动地将这个sessionId 附加在请求报文中 ,服务器收到请求之后就会根据sessionId找到对应的
session,从而再次使用,使得用户的状态得以保持。
每个session都有一个sessionId,这个ID存放有两种方式:
1、通过URL存取,比如:Java程序中,URL会带上一个jsessionId=xxxxxx等,这样每次重新请求的时候都传了sessionId给服务器。
2、通过cookie存取(Tomcat默认如此),这种cookie是session cookie,区别于persistent cookies也就是我们常说的cookie,session cookie要注意的是存储在浏览器内存中,而不是写到硬盘上。程序一开始执行,服务器就生成一个sessionId并通过cookie携带客户端浏览器的缓存中,当下一次访问的时候,服务器先检测一下是否有这个cookie,如果有就取它的ID,如果没有就再生成一个。这就是为什么关闭浏览器之后,再进去session已经没有了,其实在服务器端session并没有清空,而是sessionId变了。
当将浏览器关闭,服务器保存的session数据不是立即释放的,此时数据还会存在一段时间(可以在程序中加以设置,Tomcat默认15分钟),只要我们知道那个sessionId,就可以继续通过请求获得此session的信息。session里面的数据都放在服务器端,通过sessionId保证不会访问错误,服务端自动对session进行管理,如果在规定的时间内没有访问,则释放掉这个session。
补充:
1、sessionId通常在浏览器地址中是看不到的,但是当我们把浏览器的cookie禁止之后,Web服务器会采用URL重写的方式传递sessionId,这样就可以在地址栏看到sessionId了。
2、session cookie不可以跨窗口使用,但可以跨同一个窗口的多个标签页。
范例: 利用php来生成session id
# phpsessid.php
<?php
session_start();
echo session_id();
?>
5m6sm2ap7v1ulmdt22d0hi9m0q
php的session id, 如果按照每一位都是大小写和数字中的任意一个, 那么session id一共26位, 所以是35^26次幂种可能
使用同一个浏览器再次访问相同的页面, 会自动携带首次访问时从服务器端接收到的session id, 作为自己的身份信息
服务端收到请求报文后, 会查看session id, 如果浏览器携带的session id, 在服务端存在的话, 那么服务端就会把该session id对应的数据返回给浏览器, 但是并不会把 该session id再次放到响应报文里发送回客户端
4.4.3 Cookie和Session的比较
session要配合cookie一起使用, session是由服务器端生成, 发送时只把session id添加到cookie里发送给客户端. 而完全基于cookie的方式, cookie里带了大量的信息. 相当于表的一行, 而session id就相当于表的主键.
session是服务器在内存中生成的, 同一个浏览器不同时间段访问同一个网站时, session有可能发生变化, 因为session有又下起, 是会话级的.
客户端浏览器的session是临时的, 不会保存, 会话关闭了或者页面打开一段时间后, 客户端的session信息就丢失了, 下次再访问就不会携带session信息.
案例: 网页长时间不刷新会显示session end, 手机软件切出去再打开还要重新登录等场景
用户再次访问时, 由于没有session信息, 那么服务器端会生成一个新的session信息给用户.
服务器端保存的旧session还是会保留, 因为服务器无论是用胖cookie还是瘦cookie都会保留一份发给客户端的cookie在本地.
不过服务器端的cookie/session信息页是有期限的(通过后端程序设定), 超过有效期也会删除无效的session信息
cookie和session的相同和不同:
- cookie通常是在服务器生成,但也可以在客户端生成,session是在服务器端生成的
- session 将数据信息保存在服务器端,可以是内存,文件,数据库等多种形式, cookie 将数据保存在客户端的内存或文件中
- 单个cookie保存的数据不能超过4K,每个站点cookie个数有限制,比如IE8为50个、Firefox为50个、Opera为30个;session存储在服务器,没有容量限制
- cookie存放在用户本地,可以被轻松访问和修改,安全性不高;session存储于服务器,比较安全cookie有会话cookie和持久cookie,生命周期为浏览器会话期的会话cookie保存在缓存,关闭浏览器窗口就消失,持久cookie被保存在硬盘,知道超过设定的过期时间;随着服务端session存储压力增大,会根据需要定期清理session数据
- session中有众多数据,只将sessionID这一项可以通过cookie发送至客户端进行保留,客户端下次访问时,在请求报文中的cookie会自动携带sessionID,从而和服务器上的的session进行关联
cookie缺点:
1、使用cookie来传递信息,随着cookie个数的增多和访问量的增加,它占用的网络带宽也很大,试想假如cookie占用200字节,如果一天的PV有几个亿,那么它要占用多少带宽?
2、cookie并不安全,因为cookie是存放在客户端的,所以这些cookie可以被访问到,设置可以通过插件添加、修改cookie。所以从这个角度来说,我们要使用sesssion,session是将数据保存在服务端的,只是通过cookie传递一个sessionId而已,所以session更适合存储用户隐私和重要的数据
session 缺点:
1、不容易在多台服务器之间共享,可以使用session绑定,session复制,session共享解决
2、session存放在服务器中,所以session如果太多会非常消耗服务器的性能cookie和session各有优缺点,在大型互联网系统中,单独使用cookie和session都是不可行的
5. Web工具和命令
5.1 links和elinks
links命令: 命令行浏览器, CentOS8以后好像被删除了, 只在CentOS6和7上使用过.
在CentOS6上, links和elink是软链接, 在CentOS7上二者不是软链接, 但功能类似
格式:
links [OPTION]... [URL]...
常用选项:
- -dump 非交互式模式,显示输出结果. 直接将网页显示为纯文字
- -source 打印源码, 直接将网页的源码打印到终端. 即使源网页不允许复制, 也可以把源码打印下来
范例: links访问网站
[23:44:22 root@centos-7-1 ~]#yum -y install links
[23:44:33 root@centos-7-1 ~]#links
按ESC键可以进行选择进入的某个URL
或者可以直接links 10.0.0.86
, 效果一样
范例: links抓取不让复制的页面, 获取网页html的源代码
# links.html
[23:48:40 root@CentOS-8-6 /var/www/html]#vim links.html
<h1>/var/www/html/links.html</h1>
<p>This is P</p>
<div>This is div1</div>
<div>This is div2</div>
# 不加-source选项就是纯粹的作为命令行浏览器
[23:50:52 root@centos-7-1 ~]#links 10.0.0.86/links.html
# 加-source选项会直接把网页的源码打印在终端
[23:52:29 root@centos-7-1 ~]#links 10.0.0.86/links.html -source
范例: links -dump
5.2 wget
wget命令:
常用选项:
-q 静默模式
-c 断点续传
-P /path 保存在指定目录
-O filename 保存为指定文件名,filename 为 – 时,发送至标准输出
--limit-rate= 指定传输速率,单位K,M等
-P
:
- 将URL指向的文件, 下载到指定的目录下, 不能修改下载的文件名\
-
-P
后面接的需要是一个目录, 目录用不用/
补全效果都一样, 如果目录不存在, 那么会自动创建, 并把文件下载到该目录里
wget -qP 目标目录 URL # 静默模式
-O
:
- 将URL中的文件, 下载到某个目录中
-
-O
后, 接的需要是一个文件名, 可以修改下载的文件名 - 如果不指定文件名而使用
-O -
, 则会把URL指向的文件内容输出打印到屏幕上
配合-q, 不显示下载过程 wget -qO - http://www.wget.com/1.txt 或者 wget -qO /data/kernel_latest http://www.wget.com/2.txt
wget -qO 路径/文件名称(可以修改或者保持原来的名字) URL
wget -qO - URL 直接输出在屏幕
wget -P 下载目录 URL
选项:
#启动
-V, –version 显示wget的版本后退出
-h, –help 打印语法帮助
-b, –background 启动后转入后台执行
-e, –execute=COMMAND 执行`.wgetrc'格式的命令,wgetrc格式参见/etc/wgetrc或~/.wgetrc
#记录和输入文件
-o, –output-file=FILE 把记录写到FILE文件中
-a, –append-output=FILE 把记录追加到FILE文件中
-d, –debug 打印调试输出
-q, –quiet 安静模式(没有输出)
-v, –verbose 冗长模式(这是缺省设置)
-nv, –non-verbose 关掉冗长模式,但不是安静模式
-i, –input-file=FILE 下载在FILE文件中出现的URLs
-F, –force-html 把输入文件当作HTML格式文件对待
-B, –base=URL 将URL作为在-F -i参数指定的文件中出现的相对链接的前缀
–sslcertfile=FILE 可选客户端证书
–sslcertkey=KEYFILE 可选客户端证书的KEYFILE
–egd-file=FILE 指定EGD socket的文件名
#下载
–bind-address=ADDRESS
指定本地使用地址(主机名或IP,当本地有多个IP或名字时使用)
-t, –tries=NUMBER 设定最大尝试链接次数(0 表示无限制).
-O –output-document=FILE 把文档写到FILE文件中
-nc, –no-clobber 不要覆盖存在的文件或使用.#前缀
-c, –continue 接着下载没下载完的文件
–progress=TYPE 设定进程条标记
-N, –timestamping 不要重新下载文件除非比本地文件新
-S, –server-response 打印服务器的回应
–spider 不下载任何东西
-T, –timeout=SECONDS 设定响应超时的秒数
-w, –wait=SECONDS 两次尝试之间间隔SECONDS秒
–waitretry=SECONDS 在重新链接之间等待1…SECONDS秒
–random-wait 在下载之间等待0…2*WAIT秒
-Y, –proxy=on/off 打开或关闭代理
-Q, –quota=NUMBER 设置下载的容量限制
–limit-rate=RATE 限定下载输率
#目录
-nd –no-directories 不创建目录
-x, –force-directories 强制创建目录
-nH, –no-host-directories 不创建主机目录
-P, –directory-prefix=PREFIX 将文件保存到目录 PREFIX/…
–cut-dirs=NUMBER 忽略 NUMBER层远程目录
#HTTP 选项
–http-user=USER 设定HTTP用户名为 USER.
–http-passwd=PASS 设定http密码为 PASS.
-C, –cache=on/off 允许/不允许服务器端的数据缓存 (一般情况下允许).
-E, –html-extension 将所有text/html文档以.html扩展名保存
–ignore-length 忽略 `Content-Length'头域
–header=STRING 在headers中插入字符串 STRING
–proxy-user=USER 设定代理的用户名为 USER
–proxy-passwd=PASS 设定代理的密码为 PASS
–referer=URL 在HTTP请求中包含 `Referer: URL'头
-s, –save-headers 保存HTTP头到文件
-U, –user-agent=AGENT 设定代理的名称为 AGENT而不是 Wget/VERSION.
–no-http-keep-alive 关闭 HTTP活动链接 (永远链接).
–cookies=off 不使用 cookies.
–load-cookies=FILE 在开始会话前从文件 FILE中加载cookie
–save-cookies=FILE 在会话结束后将 cookies保存到 FILE文件中
#FTP 选项
-nr, –dont-remove-listing 不移走 `.listing'文件
-g, –glob=on/off 打开或关闭文件名的 globbing机制
–passive-ftp 使用被动传输模式 (缺省值).
–active-ftp 使用主动传输模式
–retr-symlinks 在递归的时候,将链接指向文件(而不是目录)
#递归下载
-r, –recursive 递归下载--慎用!
-l, –level=NUMBER 最大递归深度 (inf 或 0 代表无穷).
–delete-after 在现在完毕后局部删除文件
-k, –convert-links 转换非相对链接为相对链接
-K, –backup-converted 在转换文件X之前,将之备份为 X.orig
-m, –mirror 等价于 -r -N -l inf -nr.
-p, –page-requisites 下载显示HTML文件的所有图片
#递归下载中的包含和不包含(accept/reject)
-A, –accept=LIST 分号分隔的被接受扩展名的列表
-R, –reject=LIST 分号分隔的不被接受的扩展名的列表
-D, –domains=LIST 分号分隔的被接受域的列表
–exclude-domains=LIST 分号分隔的不被接受的域的列表
–follow-ftp 跟踪HTML文档中的FTP链接
–follow-tags=LIST 分号分隔的被跟踪的HTML标签的列表
-G, –ignore-tags=LIST 分号分隔的被忽略的HTML标签的列表
-H, –span-hosts 当递归时转到外部主机
-L, –relative 仅仅跟踪相对链接
-I, –include-directories=LIST 允许目录的列表
-X, –exclude-directories=LIST 不被包含目录的列表
-np, –no-parent 不要追溯到父目录
范例: wget实现浏览器功能
[23:09:37 root@CentOS-8-6 /var/www/html]#wget -qO - http://10.0.0.86/index.html
index.html
范例:wget实现下载限速
# 下载aliyun的镜像
[root@centos8 ~]#wget --limit-rate 1M -P /data
https://mirrors.aliyun.com/centos/8/isos/x86_64/CentOS-8-x86_64-1905-dvd1.iso
--2019-12-12 13:02:18-- https://mirrors.aliyun.com/centos/8/isos/x86_64/CentOS-
8-x86_64-1905-dvd1.iso
Resolving mirrors.aliyun.com (mirrors.aliyun.com)... 27.221.92.112,
119.167.168.225, 61.240.128.248, ...
Connecting to mirrors.aliyun.com (mirrors.aliyun.com)|27.221.92.112|:443...
connected.
HTTP request sent, awaiting response... 200 OK
Length: 7135559680 (6.6G) [application/octet-stream]
Saving to: ‘/data/CentOS-8-x86_64-1905-dvd1.iso’
CentOS-8-x86_64-1905-dvd1.iso 100%
[===============================================================================
=========>] 6.65G 1.04MB/s in 1h 53m
2019-12-12 14:55:45 (1024 KB/s) - ‘/data/CentOS-8-x86_64-1905-dvd1.iso’ saved
[7135559680/7135559680]
[root@centos8 ~]#ls /data
CentOS-8-x86_64-1905-dvd1.iso
范例: wget抓取网站的目录
# wget可以把一个网站的某个目录, 或者整个目录都抓取到本地.
# 不过, 抓取的结果会包含一些垃圾文件, 比如浏览器自带的一些图片等
[root@centos8 ~]#wget -r -np -nH -R index.html http://www.example.com/dir/
[root@centos8 ~]#wget -c -r -np -k -L -p http://www.example.com/dir/
-r : 遍历所有子目录
-np : 不到上一层子目录去
-nH : 不要将文件保存到主机名文件夹
-R index.html : 不下载 index.html 文件
5.3 ab命令
ab压力测试命令:
httpd的压力测试工具:
ab, webbench, http_load, seige
Jmeter 开源
Loadrunner 商业,有相关认证
tcpcopy:网易,复制生产环境中的真实请求,并将之保存
ab 来自httpd-tools
包
常见选项:
说明:并发数高于1024时,需要用 ulimit –n # 调整能打开的文件数, 内核参数调优
-n: 模拟总的请求数
-c: 模拟并发数
-k:以持久连接模式测试
需要重点关注Requests per Second数值, 表示服务器每秒能处理的请求数
具体每秒能处理的请求数还要看服务器配置, 服务器调优, web软件版本和访问的资源
命令格式:
ab [OPTIONS] URL
范例: Too many open files
# ab测试时要补全/路径
[00:14:08 root@CentOS-8-6 ~]#ab -c 2000 -n 3000 http://10.0.0.86/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 10.0.0.86 (be patient)
socket: Too many open files (24)
# 临时调整能同时打开的文件数
# 并发2000意味着同时创建2000个连接, 开启2000个socket, 打开2000个文件
# 默认同时能打开1024
[00:14:10 root@CentOS-8-6 ~]#ulimit -n
1024
# 临时调整为10240
[00:16:13 root@CentOS-8-6 ~]#ulimit -n 10240
[00:16:41 root@CentOS-8-6 ~]#ulimit -n
10240
[00:16:42 root@CentOS-8-6 ~]#ab -c 2000 -n 3000 http://10.0.0.86/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 10.0.0.86 (be patient)
Completed 300 requests
Completed 600 requests
Completed 900 requests
Completed 1200 requests
Completed 1500 requests
Completed 1800 requests
Completed 2100 requests
Completed 2400 requests
Completed 2700 requests
Completed 3000 requests
Finished 3000 requests
Server Software: Apache/2.4.37
Server Hostname: 10.0.0.86
Server Port: 80
Document Path: /
Document Length: 11 bytes
Concurrency Level: 2000
Time taken for tests: 1.052 seconds
Complete requests: 3000
Failed requests: 0
Total transferred: 813000 bytes
HTML transferred: 33000 bytes
Requests per second: 2852.79 [#/sec] (mean) # 服务器每秒能处理的请求数
Time per request: 701.068 [ms] (mean)
Time per request: 0.351 [ms] (mean, across all concurrent requests)
Transfer rate: 754.99 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 7 14.4 2 52
Processing: 13 54 149.5 21 998
Waiting: 2 52 149.6 20 998
Total: 16 61 158.1 24 1048
Percentage of the requests served within a certain time (ms)
50% 24
66% 38
75% 41
80% 42
90% 83
95% 95
98% 1044
99% 1044
100% 1048 (longest request)
5.4 curl
curl命令:
常见选项:
-A/--user-agent <string> 设置用户代理发送给服务器
-e/--referer <URL> 来源网址
--cacert <file> CA证书 (SSL)
-k/--insecure 允许忽略证书进行 SSL 连接
--compressed 要求返回是压缩的格式
-H/--header "key:value” 自定义首部字段传递给服务器
-i 显示页面内容,包括报文首部信息
-I/--head 只显示响应报文首部信息
-D/--dump-header <file>将url的header信息存放在指定文件中
--basic 使用HTTP基本认证
-u/--user <user[:password]>设置服务器的用户和密码
-L 如果有3xx响应码,重新发请求到新位置
-O 使用URL中默认的文件名保存文件到本地
-o <file> 将网络文件保存为指定的文件中
--limit-rate <rate> 设置传输速度
-0/--http1.0 数字0,使用HTTP 1.0
-v/--verbose 更详细
-C 选项可对文件使用断点续传功能
-c/--cookie-jar <file name> 将url中cookie存放在指定文件中
-x/--proxy <proxyhost[:port]> 指定代理服务器地址
-X/--request <command> 向服务器发送指定请求方法
-U/--proxy-user <user:password> 代理服务器用户和密码
-T 选项可将指定的本地文件上传到FTP服务器上
--data/-d 方式指定使用POST方式传递数据
-s --silent Silent mode
-b name=data 从服务器响应set-cookie得到值,返回给服务器
-w <format> 显示相应的指定的报文信息,如:%{http_code},%{remote_ip}等
-m, --max-time <time> 允许最大传输时间
范例:利用curl 获取响应码和远程主机IP
[root@ubuntu ~]#curl -s -I -m10 -o /dev/null -w %{http_code}
http://www.baidu.com/
200
[root@ubuntu ~]#curl -s -I -m10 -o /dev/null -w %{remote_ip}
http://www.xxx.com
11.11.11.11
[root@centos8 ~]#curl -s -I -m10 -o /dev/null -w %{local_ip}
http://www.yyy.com
10.10.10.10
[root@centos8 ~]#curl -s -I -m10 -o /dev/null -w %{local_port}
http://www.zzz.com
13321
[root@centos8 ~]#curl -s -I -m10 -o /dev/null -w %{remote_port}
http:///www.aaa.com
8080
范例: curl命令使用
[root@centos8 ~]#curl -I http://www.163.com
HTTP/1.1 403 Forbidden
Date: Thu, 12 Dec 2019 01:18:11 GMT
Content-Type: text/html
Content-Length: 234
Connection: keep-alive
Server: web cache
Expires: Thu, 12 Dec 2019 01:18:11 GMT
X-Ser: BC14_lt-tianjin-tianjin-3-cache-3
Cache-Control: no-cache,no-store,private
cdn-user-ip: 123.118.223.243
cdn-ip: 125.39.21.14
X-Cache-Remote: HIT
cdn-source: baishan
[root@centos8 ~]#curl -I -A ie10 http://www.163.com
HTTP/1.1 200 OK
Date: Thu, 12 Dec 2019 01:19:30 GMT
Content-Type: text/html; charset=GBK
Connection: keep-alive
Expires: Thu, 12 Dec 2019 01:20:45 GMT
Server: nginx
Cache-Control: no-cache,no-store,private
Age: 5
Vary: Accept-Encoding
X-Ser: BC20_dx-lt-yd-fujian-xiamen-8-cache-2, BC57_dx-lt-yd-fujian-xiamen-8-
cache-2, BC5_lt-tianjin-tianjin-3-cache-3, BC13_lt-tianjin-tianjin-3-cache-3
cdn-user-ip: 123.118.223.243
cdn-ip: 125.39.21.13
X-Cache-Remote: HIT
cdn-source: baishan
# curl命令冒充firefox浏览器, 利用的是请求报文的首部user-agent字段
# -H选项可以自定义请求报文的首部字段和值, 然后传递给服务器
[root@centos6 ~]#curl -H "user-agent: firefox" 10.0.0.86
10.0.0.237 - - [14/Dec/2022:00:10:09 +1100] "GET / HTTP/1.1" 200 11 "-" "firefox"