Ⅱ、响应格式
HTTP响应由以下四部分组成:
- 状态行:[http版本]+[状态码]+[状态码描述]
- 响应报头:响应的属性,这些属性都是以key: value的形式按行陈列的。
- 空行:遇到空行表示响应报头结束。
- 响应正文:响应正文允许为空字符串,如果响应正文存在,则响应报头中会有一个Content-Length属性来标识响应正文的长度。比如服务器返回了一个html页面,那么这个html页面的内容就是在响应正文当中的。
为了证明上面的结论我们可以通过telnet
发送的请求,来得到响应,这里我们以百度的响应为例子。
我们先通过telnet
连接上百度,然后对百度发送一个请求,然后百度会将响应结果发送回来,这里我们对百度的Web根目录发起请求,它应该给我们显示百度前端界面的html
文件。
在响应报头里面有几个很重要的字段,这些字段不仅响应里面含有,请求里面也含有。
Content-Length
:其表示的是响应正文的长度,通过这个字段双方能够确定对方的请求/响应正文的长度是多少,方便正确的进行提取。Content-Type
: 表示正文中数据类型(例如html,png,jpg,MP3,MP4)。
为什么HTTP请求和响应都要有交互版本?
HTTP请求当中的请求行和HTTP响应当中的状态行,当中都包含了http的版本信息。其中请求格式中HTTP的版本是客户端的HTTP版本,而响应格式的HTTP版本是服务器HTTP版本。
客户端和服务器双方在进行通信时会交互双方http版本,主要还是为了兼容性的问题。因为服务器和客户端使用的可能是不同的http版本,为了让不同版本的客户端都能享受到对应的服务,此时就要求通信双方需要进行版本协商。
客户端在发起HTTP请求时告诉服务器自己所使用的http版本,此时服务器就可以根据客户端使用的http版本,为客户端提供对应的服务,而不至于因为双方使用的http版本不同而导致无法正常通信。因此为了保证良好的兼容性,通信双方需要交互一下各自的版本信息是很有必要的。
二、HTTP的方法
HTTP常见的方法如下:
方法 | 说明 | 支持的HTTP协议版本 |
GET | 获取资源 | 1.0、1.1 |
POST | 传输实体主体 | 1.0、1.1 |
PUT | 传输文件 | 1.0、1.1 |
HEAD | 获得报文首部 | 1.0、1.1 |
DELETE | 删除文件 | 1.0、1.1 |
OPTIONS | 询问支持的方法 | 1.1 |
TRACE | 追踪路径 | 1.1 |
CONNECT | 要求用隧道协议连接代理 | 1.1 |
LINK | 建立和资源之间的联系 | 1.0 |
UNLINK | 断开连接关系 | 1.0 |
其中最常用的就是就是GET方法和POST方法,我们这里也只介绍这两种方法。
1、GET方法
GET方法一般用于获取某种资源信息,但上传数据时可以使用GET方法。
GET方法是通过url传参的,这种传参的方式不够私密(注意:不是不够安全),而且因为url的长度是有限制的,所以如果要传递的数据很多,就不能够使用GET方法。
Postman软件演示GET传参
如果访问我们的服务器时使用的是GET方法,此时应该通过url进行传参,可以在Params下进行参数设置,因为Postman当中的Params就相当于url当中的参数,你在设置参数时可以看到对应的url也在随之变化。
我们的服务器收到的HTTP请求时,可以看到请求行中的url就携带上了我们刚才在Postman当中设置的参数。
2、POST方法
POST方法一般用于将数据上传给服务器。
POST方法是通过正文传参的,因为理论上正文的长度是可以无限大的,所以POST方法就可以携带更多的数据。
此外POST方法传参更加私密,因为POST方法不会将你的参数回显到url当中,此时也就不会被别人轻易看到。
Postman软件演示GET传参
这里我们使用的是POST方法,此时就应该通过正文进行传参,可以在Body下进行参数设置,在设置时可以选中Postman当中的raw方式传参,表示原始传参,也就是你输入的参数是什么样的实际传递的参数就是什么样的。
此时服务器收到的HTTP请求的请求正文就不再是空字符串,而是我们通过正文传递的参数。
所以所有的登陆注册支付等行为,一般都要使用POST方法提参,但是POST方法和GET方法实际都不安全,要做到安全只能通过加密来完成。
三、HTTP的状态码
HTTP的状态码如下:
类别 | 原因短语 | |
1XX | Informational(信息性状态码) | 接收的请求正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
最常见的状态码比如:200(OK),404(Not Found),403(Forbidden请求权限不够),302(Redirect ,重定向),504(Bad Gateway)
1、重定向功能
这里我们重点介绍Redirect,重定向功能。
HTTP中的重定向就是当客户端访问一个服务器的资源后,服务器会返回一个响应包,响应包中带有另一个链接,然后浏览器会自动跳转到另一个链接,无需用户点击,是浏览器自动跳转的,此时这个服务器相当于提供了一个引路的服务。
重定向又可分为临时重定向和永久重定向,其中状态码301表示的就是永久重定向,而状态码302和307表示的是临时重定向。
临时重定向和永久重定向本质是影响客户端的标签,决定客户端是否需要更新目标地址。如果某个网站是永久重定向,那么第一次访问该网站时由浏览器帮你进行重定向,但后续再访问该网站时就不需要浏览器再进行重定向了,此时你访问的直接就是重定向后的网站。而如果某个网站是临时重定向,那么每次访问该网站时如果需要进行重定向,都需要浏览器来帮我们完成重定向跳转到目标网站。
2、实际使用
进行重定向时需要用到Location
字段,Location
字段是HTTP报头当中的一个属性信息,该字段表明了你所要重定向到的目标网站,其后面跟的就是重定向后的地址。
临时重定向
// - 1构建请求行 std::string response = "HTTP/1.0 302 Found" + tol::Sep; // - 2构建响应报头 response += "Location: https://www.csdn.net" + tol::Sep; // - 3构建空行 response += tol::Sep;
永久重定向
// - 1构建请求行 std::string response = "HTTP/1.0 301 Moved Permanently" + tol::Sep; // - 2构建响应报头 response += "Location: https://www.csdn.net" + tol::Sep; // - 3构建空行 response += tol::Sep;
实际应用
重定向可实现许多目标:
- 域名别称,扩大站点的用户覆盖面。
假如站点位于www.example.com
域名下,那么通过example.com
也应该可以访问到。这种情况下,可以建立从example.com
的页面到www.example.com
的重定向。此外还可以提供你域名常见的同义词,或者该域名容易导致的拼写错误的别称来重定向到你的网站。 - 站点维护或停机期间的临时重定向。
四、HTTP常见Header
- Content-Type: 数据类型(text/html等)
- Content-Length: Body的长度
- Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
- User-Agent: 声明用户的操作系统和浏览器版本信息;
- Referer: 当前页面是从哪个页面跳转过来的;
- Connection: 链接状态。
- Location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;
- Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能。
1、Host字段
Host字段表明了客户端要访问的服务的IP和端口,比如当浏览器访问我们的服务器时,浏览器发来的HTTP请求当中的Host字段填的就是我们的IP和端口。但客户端不就是要访问服务器吗?为什么客户端还要告诉服务器它要访问的服务对应的IP和端口?
因为有些服务器实际提供的是一种代理服务,也就是代替客户端向其他服务器发起请求,然后将请求得到的结果再返回给客户端。在这种情况下客户端就必须告诉代理服务器它要访问的服务对应的IP和端口,此时Host提供的信息就有效了。
2、User-Agent
User-Agent代表的是客户端对应的操作系统和浏览器的版本信息。
比如当我们用电脑下载某些软件时,它会自动向我们展示与我们操作系统相匹配的版本,这实际就是因为我们在向目标网站发起请求的时候,User-Agent字段当中包含了我们的主机信息,此时该网站就会向你推送相匹配的软件版本。
3、Referer
Referer代表的是你当前是从哪一个页面跳转过来的。Referer记录上一个页面的好处一方面是方便回退,另一方面可以知道我们当前页面与上一个页面之间的相关性。
4、Connection
Keep-Alive(长连接)
HTTP/1.0是通过request&response的方式来进行请求和响应的,HTTP/1.0常见的工作方式就是客户端和服务器先建立链接,然后客户端发起请求给服务器,服务器再对该请求进行响应,然后立马断开连接。
但如果一个连接建立后客户端和服务器只进行一次交互,就将连接关闭,就太浪费资源了,因此现在主流的HTTP/1.1是支持长连接的。所谓的长连接就是建立连接后,客户端可以不断的向服务器一次写入多个HTTP请求,而服务器在上层依次读取这些请求就行了,此时一条连接就可以传送大量的请求和响应,这就是长连接。
如果HTTP请求或响应报头当中的Connect字段对应的值是Keep-Alive,就代表支持长连接。
close
表明客户端或服务器想要关闭该网络连接,这是 HTTP/1.0 请求的默认值
5、Cookie和Session
HTTP 是一种无状态 (stateless) 协议, HTTP协议本身不会对发送过的请求和相应的通信状态进行持久化处理。这样做的目的是为了保持HTTP协议的简单性,从而能够快速处理大量的事务, 提高效率。
然而,在许多应用场景中,我们需要保持用户登录的状态或记录用户购物车中的商品。由于HTTP是无状态协议,所以必须引入一些技术来记录管理状态,例如Cookie。
Cookie的应用 :
当我们第一次登录某个网站时,需要输入我们的账号和密码进行身份认证,此时如果服务器经过数据比对后判定你是一个合法的用户,那么为了让你后续在进行某些网页请求时不用重新输入账号和密码,此时服务器就会进行Set-Cookie的设置。(Set-Cookie也是HTTP报头当中的一种属性信息)
当认证通过并在服务端进行Set-Cookie设置后,服务器在对浏览器进行HTTP响应时就会将这个Set-Cookie响应给浏览器。而浏览器收到响应后会自动提取出Set-Cookie的值,将其保存在浏览器的cookie文件当中,此时就相当于我们的账号和密码信息保存在本地浏览器的cookie文件当中。
从第一次登录认证之后,浏览器再向该网站发起的HTTP请求当中就会自动包含一个cookie字段,其中携带的就是我第一次的认证信息,此后对端服务器需要对你进行认证时就会直接提取出HTTP请求当中的cookie字段,而不会重新让你输入账号和密码了。
也就是在第一次认证登录后,后续所有的认证都变成了自动认证,这就是cookie技术。
Cookie 的类型 :
cookie
就是在浏览器当中的一个小文件,文件里记录的就是用户的私有信息。cookie文件可以分为两种,一种是内存级别的cookie文件,另一种是文件级别的cookie文件。
将浏览器关掉后再打开,访问之前登录过的网站,如果需要你重新输入账号和密码,说明你之前登录时浏览器当中保存的cookie信息是内存级别的。
将浏览器关掉甚至将电脑重启再打开,访问之前登录过的网站,如果不需要你重新输入账户和密码,说明你之前登录时浏览器当中保存的cookie信息是文件级别的。
Session的引入:
网络中有一些病毒或者钓鱼链接,它们可能盗取你的Cookie信息,如果你浏览器当中保存的cookie信息被非法用户盗取了,那么此时这个非法用户就可以用你的cookie信息,以你的身份去访问你曾经访问过的网站,所以单纯的使用cookie是非常不安全的,因为此时cookie文件当中就保存的是你的私密信息,一旦cookie文件泄漏你的隐私信息也就泄漏,所以当前主流的服务器还引入了Session
这样的概念。
Session的工作原理 :
当我们第一次登录某个网站输入账号和密码后,服务器认证成功后还会服务端创建一个Session对象(可以是内存级别也可以是文件级别),这个对象里面包含了我们用户的相关信息,此外生成Session对象都有一个唯一的SessionID,系统会将所有用户的SessionID值统一维护起来。
此时当认证通过后服务端在对浏览器进行HTTP响应时,就会将这个生成的SessionID值响应给浏览器。浏览器收到响应后会自动提取出SessionID的值,将其保存在浏览器的cookie文件当中(此时Cookie保存的只有SessionID,而没有用户的相关信息,相关信息在服务器中的Session对象中),后续访问该服务器时,对应的HTTP请求当中就会自动携带上这个SessionID。
而服务器识别到HTTP请求当中包含了SessionID,就会提取出这个SessionID,然后再到对应的集合当中进行对比,对比成功就说明这个用户是曾经登录过的,此时也就自动就认证成功了,然后就会正常处理你发来的请求,这就是我们当前主流的工作方式。
正确的理解安全:
引入SessionID之后,浏览器当中的cookie文件保存的是SessionID,此时这个cookie文件同样可能被盗取。此时用户的账号和密码虽然不会泄漏了,但用户对应的SessionID是会泄漏的,非法用户仍然可以盗取我的SessionID去访问我曾经访问过的服务器,相当于还是存在刚才的问题。
- 之前的工作方式就相当于把账号和密码信息在浏览器当中再保存一份,每次请求时都自动将账号和密码的信息携带上,但是账号和密码一直在网当中发送太不安全了。
- 因此现在的工作方式是,服务器只有在第一次认证的时候需要在网络中传输账号和密码,此后在网络上发送的都是SessionID。
这种方法虽然没有真正解决安全问题,但这种方法是相对安全的。互联网上是不存在绝对安全这样的概念的,任何安全都是相对的,就算你将发送到网络当中的信息进行加密,也有可能被别人破解。
不过在安全领域有一个准则:如果破解某个信息的成本已经远远大于破解之后获得的收益(说明做这个事是赔本的),那么就可以说这个信息是安全的。
引入SessionID后的好处 :
- 在引入SessionID之前,用户登录的账号信息都是保存在浏览器内部的,此时的账号信息是由客户端去维护的。
- 而引入SessionID后,用户登录的账号信息是有服务器去维护的,在浏览器内部保存的只是SessionID
此时虽然SessionID可能被非法用户盗取,但服务器也可以使用各种各样的策略来保证用户账号的安全。
- IP是有归类的,可以通过IP地址来判断登录用户所在的地址范围。如果一个账号在短时间内登录地址发送了巨大变化,此时服务器就会立马识别到这个账号发生异常了,进而在服务器当中清除对应的Session对象。这时当你或那个非法用户想要访问服务器时,就都需要重新输入账号和密码进行身份认证,而只有用户自己是知道自己的密码的,当你重新认证登录后服务器就可以将另一方识别为非法用户,进而对该非法用户进行对应的黑名单/白名单认证。
- 当操作者想要进行某些高权限的操作时,会要求操作者再次输入账号和密码信息,再次确认身份。就算你的账号被非法用户盗取了,但非法用户在改你密码时需要输入旧密码,这是非法用户在短时间内无法做到的,因为它并不知道你的密码。这也就是为什么账号被盗后还可以找回来的原因,因为非法用户无法在短时间内修改你的账号密码,此时你就可以通过追回的方式让当前的SessionID失效,让使用该账号的用户进行重新登录认证。
- SessionID也有过期策略,比如SessionID是一个小时内是有效的。所以即便你的SessionID被非法用户盗取了,也仅仅是在一个小时内有效,而且在功能上受约束,所以不会造成太大的影响。
实验演示 :
当浏览器访问我们的服务器时,如果服务器给浏览器的HTTP响应当中包含Set-Cookie字段,那么当浏览器再次访问服务器时就会携带上这个cookie信息。
因此我们可以在服务器的响应报头当中添加上一个Set-Cookie字段,看看浏览器第二次发起HTTP请求时是否会带上这个Set-Cookie字段。
// 服务器端 std::string response = "HTTP/1.0 200 OK" + tol::Sep; // 设置Cookie response += "Set-Cookie: 1234aaaa" + tol::Sep; response += tol::Sep;
运行服务器后,用浏览器访问我们的服务器,此时通过Fiddler可以看到我们的服务器发给浏览器的HTTP响应报头当中包含了这个Set-Cookie字段。
同时我们也可以在浏览器当中看到这个cookie,此时浏览器当中就写入了这样的一个cookie。
然后我们进行刷新网页,这相当于第二次访问我们的服务器,此时通过Fiddler可以看到,第二次的HTTP请求当中会携带上这个cookie信息。