什么是超文本传输协议?
超文本传输协议(英语:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础。
通过HTTP或HTTPS协议请求的资源由统一资源标识符(Uniform Resource Identifiers,URI)来标识。
协议概述
HTTP是一个客户端(client)和服务端(server)之间请求和应答的标准,通常使用TCP协议。通过使用浏览器、网络爬虫或者其他工具,客户端发起一个HTTP请求到服务器上的指定端口(默认端口为80)。我们称这个客户端为用户代理程序(user agent)。应答的服务器上存储着一些资源,比如HTML文件或者图像。我们称这个应答服务器为源服务器(origin server)。在用户代理或者源服务器中间可能存在多个“中间层”,比如代理服务器、网关或者隧道。
通常,由HTTP客户端(client)发起一个请求,创建一个到服务器(server)指定端口的TCP连接。HTTP服务器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态,比如“HTTP/1.1 200 OK”,以及返回的内容,如请求的文件、错误信息或者其它信息。
请求方法
HTTP/1.1协议中一共定义了八种方法(也叫“动作”)来以不同方式操作指定的资源:
Get
向指定的资源发出“显示”请求。使用GET方法应该只用在读取资料,而不应当被用于产生“副作用”的操作中。
Post
向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有。每次提交,表单的数据被浏览器用编码到HTTP请求的body里。浏览器发出的POST请求的body主要有两种格式,一种是application/x-www-form-urlencoded用来传输简单的数据,另外一种是传文件,会采用multipart/form-data格式。
Put
向指定资源位置上传其最新内容。一般用作修改资源内容
Delete
请求服务器删除Request-URI所标识的资源。
Head
与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的信息”(元信息或称元数据)。
Trace
回显服务器收到的请求,主要用于测试或诊断。
Options
这个方法可使服务器传回该资源所支持的所有HTTP请求方法。用’*'来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作。
Connect
HTTP/1.1协议中预留给能够将连接改为隧道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器)。
方法名称是区分大小写的。HTTP服务器至少应该实现GET和HEAD方法,其他方法都是可选的。对于GET和HEAD方法而言,除了进行获取资源信息外,这些请求不应当再有其他意义。也就是说,这些方法应当被认为是“安全的”。 客户端可能会使用其他“非安全”方法,例如POST,PUT及DELETE,应该以特殊的方式(通常是按钮而不是超链接)告知客户可能的后果(例如一个按钮控制的资金交易),或请求的操作可能是不安全的(例如某个文件将被上传或删除)。
HTTP 流
当客户端想要和服务端进行信息交互时,过程表现为下面几步:
-
打开一个TCP连接:TCP连接被用来发送一条或者多条请求,以及接受响应消息。
-
发送一个 HTTP 报文:HTTP 报文(在 HTTP/2 之前)是语义可读的。
GET / HTTP/1.1 Host: developer.mozilla.org Accept-Language: fr
-
读取服务端返回的报文消息:
HTTP/1.1 200 OK Date: Sat, 09 Oct 2010 14:28:02 GMT Server: Apache Last-Modified: Tue, 01 Dec 2009 20:18:22 GMT ETag: "51142bc1-7449-479b075b2891b" Accept-Ranges: bytes Content-Length: 29769 Content-Type: text/html <!DOCTYPE html... (here comes the 29769 bytes of the requested web page)
-
关闭连接或者为后续请求重用连接。
HTTP 报文
HTTP/1.1 以及更早的 HTTP 协议报文都是语义可读的。
有两种 HTTP 报文的类型,请求与响应,每种都有其特定的格式。
请求
请求元素:
- HTTP的请求方法(Method):经常是由一个动词GET、POST或者名词OPTIONS、HEAD来定义客户端的动作行为。
- 要获取的资源的路径(Path):通常是上下文中就很明显的元素资源的URL,它没有protocol(
http://
),domain(developer.mozilla.org
)或是TCP的port(80)。 - HTTP协议的版本号(Version of the protocol)。
- 为服务端表达其他信息的可选标头(Headers)。
- 对于一些像POST这样的方法,报文的主体(body)就包含了发送的资源,这与响应报文的主体类似。
响应
响应元素:
- HTTP协议版本号(Version of the protocol)。
- 状态码(Status code):来告知对应请求执行成功或失败,以及失败的原因。
- 状态信息(Status message):这个信息是非权威的状态码描述信息,可以由服务端自行设定。
- HTTP标头(Headers):与请求标头类似。
- 可选项,比起请求报文,响应报文中更常见地包含获取资源的主体。
无状态、cookie、session、Token
什么是无状态
在同一个连接中,两个执行成功的请求之间是没有关系的。这就会带来一个问题,用户没有办法在同一个网站中进行连续的交互。比如在一个电商网站里,用户想分两次想把某个商品加入到购物车中,这两次添加商品的操作是没有关联的,服务端其实是没办法知道这两次添加商品其实都是同一个用户在同一次添加的。
使用 HTTP Cookie 可以解决这个问题。
无状态的好处
减少服务端的存储开销:服务端不需要存储请求的状态信息。
无状态的坏处
服务器没有记忆能力,无法完成一些需要关联性的操作。如同一用户两次往购物车添加商品。
Cookie
HTTP Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据。浏览器会存储 cookie 并在下次向同一服务器再发起请求时携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器——如保持用户登录状态。Cookie使基于无状态的HTTP协议记录稳定的状态信息成为了可能。
服务器使用Set-Cookie
向client发送Cookie信息,可能像这样:
Set-Cookie: <cookie-name>=<cookie-value>
事例表明服务器告知客户端存储一对cookie:
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
接下来,对该服务器发起的每一次新请求,浏览器都会将之前保存的Cookie信息通过header再发送给服务器:
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
限制访问Cookie
有两种方法可以确保Cookie
被安全发送,并且不会被意外的参与者或脚本访问:Secure
属性和HttpOnly
属性。
Secure:有此标记的Cookie只应通过被HTTPS协议加密过的请求发送给服务器。它永远不会使用不安全的HTTP发送(本地主机除外),不安全的站点(在URL中带有http:
)无法使用带有Secure
属性的cookie。但是,Secure
不会阻止对cookie中敏感信息的访问。例如,有权访问客户端硬盘的人可以读取和修改它。
HttpOnly:JavaScript的Document.cookie
API无法访问带有HttpOnly
属性的cookie;此类cookie仅作用于服务器。
Session
基于cookie实现的,session存储在服务器端,sessionId会被存储到客户端的cookie中。
- 用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的Session。
- 请求返回时将此Session的唯一标识信息SessionId返回给浏览器
- 浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名
- 当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。
安全性不同
Session 比 Cookie 安全,Session 是存储在服务器端的,Cookie 是存储在客户端的。
存值的数据类型不同
Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。
有效期不同
Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。
存储大小不同
单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。
Token
token的身份验证流程:
- client使用用户名和密码请求登录
- server收到请求,去验证用户名与密码
- 验证成功后,server会签发一个token并下发给client
- client接收到token,会将token存储起来,如:存到cookie或localStorage
- client每次向server发送请求资源都携带token
- server接收到来自client的token并且验证它。
Tips
服务端不需要存储token,只需要使用固定的算法解析token。利用解析Token的资源换取存储Token的存储空间。以时间换空间,减轻服务器的压力。
状态码
常见的状态码
状态码 | 英文名称 | 中文描述 |
---|---|---|
200 | OK | 请求成功 |
301 | Moved Permanently | 资源(网页等)被永久转移到其他URL |
400 | Bad Request | 客户端请求的语法错误,服务器无法解析 |
404 | Not Found | 请求的资源(网页等)不存在 |
500 | Internal Server Error | 服务器内部错误 |
状态码分类
分类 | 描述 |
---|---|
1xx | 信息,服务器收到请求,需要请求者继续进行操作 |
2xx | 成功,操作被成功接受并处理 |
3xx | 重定向,需要进一步的操作以完成请求 |
4xx | 客户端错误,请求包含语法错误或无法完成请求 |
5xx | 服务器错误,服务器在处理请求的过程中发生了错误 |
浏览器同源策略
同源的定义
如果两个URL的协议,端口和主机都相同的,那么这两个URL同源。
跨域资源共享CORS
一种基于HTTP头的机制,该机制通过允许服务器标示除了它自己以外的其它源(域、协议或端口),使得浏览器允许这些源访问加载自己的资源。
对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET
以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST
请求),浏览器必须首先使用 OPTIONS
方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨源请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(例如 Cookie 和 HTTP 认证相关数据)。
预检请求preflight request
“需预检的请求”要求必须首先使用OPTIONS
发起一个预检请求到服务器,以获知服务器是否允许该实际请求。“预检请求”的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
附带身份凭证的请求
在响应附带身份凭证的请求时:
- 服务器不能将
Access-Control-Allow-Origin
的值设为通配符“*
”,而应将其设置为特定的域,如:Access-Control-Allow-Origin: https://example.com
。 - 服务器不能将
Access-Control-Allow-Headers
的值设为通配符“*
”,而应将其设置为标头(headers)名称的列表,如:Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
。 - 服务器不能将
Access-Control-Allow-Methods
的值设为通配符“*
”,而应将其设置为特定请求方法名称的列表,如:Access-Control-Allow-Methods: POST, GET
。
HTTP Response Header 字段
header key | header value | describe |
---|---|---|
Access-Control-Allow-Origin | <origin> | * | 允许浏览器访问的资源列表 |
Access-Control-Allow-Headers | <header-name>[, <header-name>]* | 允许浏览器访问的header列表 |
Access-Control-Allow-Methods | <method>[, <method>]* | 指定访问资源时允许使用的请求方法 |
Access-Control-Max-Age | <delta-seconds> | 指定preflight预检请求能够被缓存多久 |
Access-Control-Expose-Headers | <header-name>[, <header-name>]* | 允许浏览器访问的额外header列表 |
Access-Control-Allow-Credentials | true | 指定浏览器的credentials 是否允许浏览器读取response的内容 |