震惊 HTTP 在疫情期间把我吓得不敢出门了(二)

简介: 这是 HTTP 系列的第三篇文章,此篇文章为 HTTP 的进阶文章。 在前面两篇文章中我们讲述了 HTTP 的入门,HTTP 所有常用标头的概述,这篇文章我们来聊一下 HTTP 的一些 黑科技。

代理认证

由于资源认证和代理认证可以共存,因此需要不同的头和状态码,在代理的情况下,会返回状态码 407(需要代理认证)Proxy-Authenticate 响应头包含至少一个适用于代理的情况,Proxy-Authorization请求头用于将证书提供给代理服务器。下面分别来认识一下这两个标头

Proxy-Authenticate

HTTP Proxy-Authenticate 响应标头定义了身份验证方法,应使用该身份验证方法来访问代理服务器后面的资源。它将请求认证到代理服务器,从而允许它进一步发送请求。例如

Proxy-Authenticate: Basic
Proxy-Authenticate: Basic realm="Access to the internal site"

Proxy-Authorization

这个 HTTP 请求标头和上面的 Proxy-Authenticate 拼接很相似,但是概念不同,这个标头用于向代理服务器提供凭据,例如

Proxy-Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l

下面是代理服务器的请求/响应认证过程

微信图片_20220412195627.jpg

这个过程和通用的过程类似,我们就不再详细展开描述了。

禁止访问

如果代理服务器收到的有效凭据不足以获取对给定资源的访问权限,则服务器应使用403 Forbidden状态代码进行响应。与 401 Unauthorized407 Proxy Authorization Required 不同,该用户无法进行身份验证。

WWW-Authenticate 和 Proxy-Authenticate 头

WWW-AuthenticateProxy-Authenticate 响应头定义了获得对资源访问权限的身份验证方法。他们需要指定使用哪种身份验证方案,以便希望授权的客户端知道如何提供凭据。它们的一般表示形式如下

WWW-Authenticate: <type> realm=<realm>

Proxy-Authenticate: <type> realm=<realm>

我想你从上面看到这里一定会好奇 <type>realm是什么东西,现在就来解释下。

  • <type> 是认证协议,Basic 是下面协议中最普遍使用的

RFC 7617 中定义了Basic HTT P身份验证方案,该方案将凭据作为用户ID /密码对传输,并使用 base64 进行编码。(感兴趣的同学可以看看 https://tools.ietf.org/html/rfc7617)

其他的认证协议主要有

认证协议 参考来源
Basic 查阅 RFC 7617,base64编码的凭据
Bearer 查阅 RFC 6750,承载令牌来访问受 OAuth 2.0保护的资源
Digest 查阅 RFC 7616,Firefox仅支持md5哈希,请参见错误bug 472823以获得SHA加密支持
HOBA 查阅 RFC 7486
Mutual 查阅 RFC 8120
AWS4-HMAC-SHA256 查阅 AWS docs
  • realm 用于描述保护区或指示保护范围,这可能是诸如 Access to the staging site(访问登陆站点) 或者类似的,这样用户就可以知道他们要访问哪个区域。

Authorization 和 Proxy-Authorization 标头

Authorization 和 Proxy-Authorization 请求标头包含用于通过代理服务器对用户代理进行身份验证的凭据。在此,再次需要类型,其后是凭据,取决于使用哪种身份验证方案,可以对凭据进行编码或加密。一般表示如下

Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
Proxy-Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l

HTTP 缓存

通过把请求/响应缓存起来有助于提升系统的性能,Web 缓存减少了延迟和网络传输量,因此减少资源获取锁需要的时间。由于链路漫长,网络时延不可控,浏览器使用 HTTP 获取资源的成本较高。所以,非常有必要把数据缓存起来,下次再请求的时候尽可能地复用。当 Web 缓存在其存储中具有请求的资源时,它将拦截该请求并直接返回资源,而不是到达源服务器重新下载并获取。这样做可以实现两个小目标

  • 减轻服务器负载
  • 提升系统性能

下面我们就一起来探讨一下 HTTP 缓存都有哪些

不同类型的缓存

HTTP 缓存有几种不同的类型,这些可以分为两个主要类别:私有缓存共享缓存

  • 共享缓存:共享缓存是一种缓存,它可以存储多个用户重复使用的请求/响应。
  • 私有缓存:私有缓存也称为专用缓存,它只适用于单个用户。
  • 不缓存过期资源:所有的请求都会直接到达服务器,由服务器来下载资源并返回。

我们主要探讨浏览器缓存代理缓存,但真实情况不只有这两种缓存,还有网关缓存,CDN,反向代理缓存和负载平衡器,把它们部署在 Web 服务器上,可以提高网站和 Web 应用程序的可靠性,性能和可伸缩性。

不缓存过期资源

不缓存过期资源即浏览器和代理不会缓存过期资源,客户端发起的请求会直接到达服务器,可以使用 no-cache 标头代表不缓存过期资源。

微信图片_20220412195633.jpg

no-cache 属于 Cache-Control 通用标头,其一般的表示方法如下

Cache-Control: no-cache

也可以使用 max-age = 0 来实现不缓存的效果。

Cache-Control: max-age=0

私有缓存

私有缓存只用来缓存单个用户,你可能在浏览器设置中看到了 缓存,浏览器缓存包含服务器通过 HTTP 下载下来的所有文档。这个高速缓存用于使访问的文档可以进行前进/后退,保存操作而无需重新发送请求到源服务器。

微信图片_20220412195636.jpg

可以使用 private 来实现私有缓存,这与 public 的用法相反,缓存服务器只对特定的客户端进行缓存,其他客户端发送过来的请求,缓存服务器则不会返回缓存。它的一般表示方法如下

Cache-Control: private

共享缓存

共享缓存是一种用于存储要由多个用户重用的响应缓存。共享缓存一般使用 public 来表示,public 属性只出现在客户端响应中,表示响应可以被任何缓存所缓存。一般表示方法如下

Cache-Control: public

微信图片_20220412195640.jpg

缓存控制

HTTP/1.1 中的 Cache-Control 常规标头字段用于执行缓存控制,使用此标头可通过其提供的各种指令来定义缓存策略。下面我们依次介绍一下这些属性

不缓存

no-store 才是真正意义上的不缓存,每次服务器接受到客户端的请求后,都会返回最新的资源给客户端。

Cache-Control: no-store

缓存但需要验证

同上面的 不缓存过期资源

私有和共享缓存

同上

缓存过期

缓存中一个很重要的指令就是max-age,这是资源被视为新鲜的最长时间 ,与 Expires 相反,此指令是相对于请求时间的。对于应用程序中不会更改的文件,通常可以添加主动缓存。下面是 mag-age 的表示

Cache-Control: max-age=31536000

缓存验证

must-revalidate 表示缓存必须在使用之前验证过时资源的状态,并且不应使用过期的资源。

Cache-Control: must-revalidate

下面是一个缓存验证图

微信图片_20220412195644.jpg

什么是新鲜的数据

一旦资源存储在缓存中,理论上就可以永远被缓存使用。但是不管是浏览器缓存还是代理缓存,其存储空间是有限的,所以缓存会定期进行清除,这个过程叫做 缓存回收(cache eviction) (自译)。另一方面,服务器上的缓存也会定期进行更新,HTTP 作为应用层的协议,它是一种客户-服务器模式,HTTP 是无状态的协议,因此当资源发生更改时,服务器无法通知缓存和客户端。因此服务器必须通过某种方式告知客户端缓存已经被更新。服务器会提供过期时间这个概念,告知客户端在此到期时间之前,资源是新鲜的,也就是未更改过的。在此到期时间的范围之外,资源已过时。过期算法(Eviction algorithms) 通常会将新资源优先于陈旧资源使用。

这里需要注意一下,过期的资源并不会被回收或忽略,当高速缓存接收到过期资源时,它会使用 If-None-Match 转发此请求,以检查它是否仍然有效。如果有效,服务器会返回 304 Not Modified响应头并且没有任何响应体,从而节省了一些带宽。

下面是使用共享缓存代理的过程

这个图应该比较好理解,只说一下 Age 的作用,Age 是 HTTP 响应标头告诉客户端源服务器在多久之前创建了响应,它的单位为,Age 标头通常接近于0,如果是0则可能是从源服务器获取的,如果不是表示可能是由代理服务器创建,那么 Age 的值表示的是缓存后的响应再次发起认证到认证完成的时间值

缓存的有效性是由多个标头来共同决定的,而并非某一个标头来决定。如果指定了 Cache-control:max-age=N ,那么缓存会保存 N 秒。如果这个通用标头不存在的话,则会检查是否存在 Expires 标头。如果 Exprires 标头存在,那么它的值减去 Date 标头的值就可以确定其有效性。最后,如果max-ageexpires 都不存在,就去寻找 Last-Modified 标头,如果存在此标头,则高速缓存的有效性等于 Date 标头的值减去 Last-modified 标头的值除以10。

缓存验证

当到达缓存资源的有效期时,将对其进行验证或再次获取。仅当服务器提供了强验证器弱验证器时,才可以进行验证。

当用户按下重新加载按钮时,将触发重新验证。如果缓存的响应包含 Cache-control:must-revalidate标头,则在正常浏览下也会触发该事件。另一个因素是 高级 -> 缓存首选项 面板中的缓存验证首选项。有一个选项可在每次加载文档时强制进行验证。

Etag

我们上面提到了强验证器和弱验证器,实现验证器功能的标头正式 Etag 的作用,这意味着 HTTP 用户代理(例如浏览器)不知道该字符串表示什么,并且无法预测其值。如果 Etag 标头是资源响应的一部分,则客户端可以在未来请求的标头中发出 If-None-Match,以验证缓存的资源。

Last-Modified响应标头可以用作弱验证器,因为它只有1秒可以分辨的时间。如果响应中存在 Last-Modified标头,则客户端可以发出 If-Modified-Since请求标头来验证缓存资源。(关于 Etag 更多我们会在条件请求介绍)

避免碰撞

通过使用 Etag 和 If-Match 标头,你可以检测避免碰撞。

例如,在编辑 MDN 时,将对当前 Wiki 内容进行哈希处理并将其放入响应中的 Etag 中

Etag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

当将更改保存到 Wiki 页面(发布数据)时,POST 请求将包含 If-Match 标头,其中包含 Etag 值以检查有效性。

If-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

如果哈希值不匹配,则表示文档已在中间进行了编辑,并返回 412 Precondition Failed 错误。

缓存未占用资源

Etag 标头的另一个典型用法是缓存未更改的资源,如果用户再次访问给定的 URL(已设置Etag),并且该 URL过时,则客户端将在 If-None-Match 标头字段中发送其 Etag 的值

If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

服务器将客户端的 Etag(通过 If-None-Match 发送)与 Etag 进行比较,以获取其当前资源版本,如果两个值都匹配(即资源未更改),则服务器会发回 304 Not Modified状态,没有主体,它告诉客户端响应的缓存仍然可以使用。

HTTP CROS 跨域

CROS 的全称是 Cross-Origin Resource Sharing(CROS),中文译为 跨域资源共享,它是一种机制。是一种什么机制呢?它是一种让运行在一个域(origin)上的 Web 应用被准许访问来自不同源服务器上指定资源的机制。在搞懂这个机制前,你需要线了解什么是 域(origin)

Origin

Web 概念中域(Origin) 的内容由scheme(protocol) - 协议host(domain) - 主机和用于访问它的 URL port - 端口定义。仅仅当 scheme 、host、port 都匹配时,两个对象才有相同的来源。这种协议相同,域名相同,端口相同的安全策略也被称为 同源策略(Same Origin Policy)。某些操作仅限于具有相同来源的内容,可以使用 CORS 取消此限制。

跨域的特点

  • 下面是跨域问题的例子,看看你是否清楚什么是跨域了

上面这两个 URL 是否具有跨域问题呢?

上面两个 URL 是不具有跨域问题的,因为这两个 URL 具有相同的协议(scheme)主机(host)

  • 那么下面这两个是否具有跨域问题呢?

这两个 URL 也不具有跨域问题,为什么不具有,端口不一样啊。其实它们两个端口是一样的。

或许你会认为这两个 URL 是不一样的,放心,关于一样不一样的论据我给你抛出来了

协议和域名部分是不区分大小写的,但是路径部分则根据服务器平台而定。Windows 和 Mac OS X 系统是不区分大小写的,而采用UNIX和Linux系的服务器系统是区分大小写的,

也就是说上面的 Example.comexample.com 其实是一个网址,并且由于两个地址具有相同的 scheme 和 host ,默认情况下服务器通过端口80传递 HTTP 内容,所以上面这两个地址也是相同的。

  • 下面这两个 URL 地址是否具有跨域问题?

这两个 URL 的 scheme 不同,所以这两个 URL 具有跨域问题

  • 再看下面这三个 URL 是否具有跨域问题

这三个 URL 也是具有跨域问题的,因为它们隶属于不通服务器的主机 host。

  • 下面这两个 URL 是否具有跨域问题

这两个 URL 也是具有跨域问题,因为这两个 URL 的默认端口不一样。

            </div>
目录
相关文章
|
存储 缓存 开发框架
震惊 HTTP 在疫情期间把我吓得不敢出门了(四)
这是 HTTP 系列的第三篇文章,此篇文章为 HTTP 的进阶文章。 在前面两篇文章中我们讲述了 HTTP 的入门,HTTP 所有常用标头的概述,这篇文章我们来聊一下 HTTP 的一些 黑科技。
94 0
震惊  HTTP 在疫情期间把我吓得不敢出门了(四)
|
Web App开发 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
总结和计划总是让人喜悦或镇痛,一方面以前一段时间没有荒废,能给现在的行动以信心,另一方面看到一年的时间并不能完成很多事情,需要抓紧时间。
614 0
|
Web App开发 监控 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
系统的升级涉及各个架构组件,细节很多。常年累月的修修补补使老系统积累了很多问题。 系统升级则意味着需要repair之前埋下的雷,那为何还要升级,可以考虑以下几个方面 成熟老系统常见问题: 1. 缺乏文档(这应该是大小公司都存在的问题。
622 0
|
Web App开发 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
最近在线上往hbase导数据,因为hbase写入能力比较强,没有太在意写的问题。让业务方进行历史数据的导入操作,中间发现一个问题,写入速度太快,并且业务数据集中到其中一个region,这个region无法split掉,处于不可用状态。
1337 0
|
Web App开发 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
Found lingering reference异常 ERROR: Found lingering reference file hdfs://jiujiang1:9000/hbase/month_hotstatic/...
720 0
|
Web App开发 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
Every Programmer Should Know These Latency Numbers 1秒=1000毫秒(ms) 1秒=1,000,000 微秒(μs) 1秒=1,000,000,000 纳秒(ns) 1秒=1,000,000,000,000 皮秒(ps) L1 cache reference .
647 0
|
Web App开发 前端开发 Java
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
java链接MongoDB处理大量数据时经常碰到cursor not found 的异常,其实是超时所致 Exception in thread "main" com.
828 0
|
Web App开发 监控 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
负载均衡: LVS(Layer 4), HAProxy(Layer 4、 7),Nginx(Layer 7) 虚拟化: LXC、KVM、Xen HA:Keepalived、Heartbeat 分布式缓存...
760 0
|
Web App开发 监控 前端开发