当前位置: 首页 > news >正文

HTTP 黑科技

🧑 博主简介:CSDN博客专家历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程高并发设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。
技术合作请加本人wx(注明来自csdn):foreast_sea

在这里插入图片描述


在这里插入图片描述

HTTP 黑科技

HTTP 进阶

  • HTTP 内容协商
    • 什么是内容协商
    • 内容协商的种类
    • 为什么需要内容协商
    • 内容协商标头
      • Accept
      • Accept-Charset
      • Accept-Language
      • Accept-Encoding
      • Content-Type
      • Content-Encoding
    • Content-Language
  • HTTP 认证
    • 通用 HTTP 认证框架
    • 代理认证
      • Proxy-Authenticate
      • Proxy-Authorization
    • 禁止访问
      • WWW-Authenticate 和 Proxy-Authenticate 头
      • Authorization 和 Proxy-Authorization 标头
  • HTTP 缓存
    • 不同类型的缓存
      • 不缓存过期资源
      • 私有缓存
      • 共享缓存
    • 缓存控制
      • 不缓存
      • 缓存但需要验证
      • 私有和共享缓存
      • 缓存过期
      • 缓存验证
    • 什么是新鲜的数据
    • 缓存验证
      • Etag
      • 避免碰撞
      • 缓存未占用资源
  • HTTP CROS 跨域
    • Origin
    • 跨域的特点
    • 同源策略
    • 跨域请求
    • 跨域功能概述
    • 访问控制
      • 简单请求
      • 预检请求
      • 带凭证的请求
      • Access-Control-Allow-Origin
      • Access-Control-Allow-Credentials
      • Access-Control-Allow-Headers
      • Access-Control-Allow-Methods
      • Access-Control-Expose-Headers
      • Access-Control-Max-Age
      • Access-Control-Request-Headers
      • Origin
  • HTTP 条件请求
    • 原则
    • 验证
      • 强验证
      • 弱验证
    • 条件请求
      • If-Match
      • If-None-Match
      • If-Modified-Since
      • If-Range
      • If-Unmodified-Since
    • 条件请求示例
      • 缓存更新
      • 断点续传
      • 通过乐观锁避免丢失更新
  • HTTP Cookies
    • 创建 Cookie
      • Set-Cookie 和 Cookie 标头
      • 会话 Cookies
      • 永久性 Cookies
      • Cookie的 Secure 和 HttpOnly 标记
    • Cookie 的作用域

这是 HTTP 系列的第三篇文章,此篇文章为 HTTP 的进阶文章。

在前面两篇文章中我们讲述了 HTTP 的入门,HTTP 所有常用标头的概述,这篇文章我们来聊一下 HTTP 的一些 黑科技

HTTP 内容协商

什么是内容协商

在 HTTP 中,内容协商是一种用于在同一 URL 上提供资源的不同表示形式的机制。内容协商机制是指客户端和服务器端就响应的资源内容进行交涉,然后提供给客户端最为适合的资源。内容协商会以响应资源的语言、字符集、编码方式等作为判断的标准。

在这里插入图片描述

内容协商的种类

内容协商主要有以下3种类型:

  • 服务器驱动协商(Server-driven Negotiation)

这种协商方式是由服务器端进行内容协商。服务器端会根据请求首部字段进行自动处理

  • 客户端驱动协商(Agent-driven Negotiation)

这种协商方式是由客户端来进行内容协商。

  • 透明协商(Transparent Negotiation)

是服务器驱动和客户端驱动的结合体,是由服务器端和客户端各自进行内容协商的一种方法。

内容协商的分类有很多种,主要的几种类型是 Accept、Accept-Charset、Accept-Encoding、Accept-Language、Content-Language

一般来说,客户端用 Accept 头告诉服务器希望接收什么样的数据,而服务器用 Content 头告诉客户端实际发送了什么样的数据。

为什么需要内容协商

我们为什么需要内容协商呢?在回答这个问题前我们先来看一下 TCP 和 HTTP 的不同。

在 TCP / IP 协议栈里,传输数据基本上都是 header+body 的格式。但 TCP、UDP 因为是传输层的协议,它们不会关心 body 数据是什么,只要把数据发送到对方就算是完成了任务。

而 HTTP 协议则不同,它是应用层的协议,数据到达之后需要告诉应用程序这是什么数据。当然不告诉应用这是哪种类型的数据,应用也可以通过不断尝试来判断,但这种方式无疑十分低效,而且有很大几率会检查不出来文件类型。

所以鉴于此,浏览器和服务器需要就数据的传输达成一致,浏览器需要告诉服务器自己希望能够接收什么样的数据,需要什么样的压缩格式,什么语言,哪种字符集等;而服务器需要告诉客户端自己能够提供的服务是什么。

所以我们就引出了内容协商的几种概念,下面依次来进行探讨

内容协商标头

Accept

接受请求 HTTP 标头会通告客户端自己能够接受的 MIME 类型

那么什么是 MIME 类型呢?在回答这个问题前你应该先了解一下什么是 MIME

MIME: MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准。MIME 消息能包含文本、图像、音频、视频以及其他应用程序专用的数据。

也就是说,MIME 类型其实就是一系列消息内容类型的集合。那么 MIME 类型都有哪些呢?

文本文件: text/html、text/plain、text/css、application/xhtml+xml、application/xml

图片文件: image/jpeg、image/gif、image/png

视频文件: video/mpeg、video/quicktime

应用程序二进制文件: application/octet-stream、application/zip

比如,如果浏览器不支持 PNG 图片的显示,那 Accept 就不指定image/png,而指定可处理的 image/gif 和 image/jpeg 等图片类型。

一般 MIME 类型也会和 q 这个属性一起使用,q 是什么?q 表示的是权重,来看一个例子

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

这是什么意思呢?若想要给显示的媒体类型增加优先级,则使用 q= 来额外表示权重值,没有显示权重的时候默认值是1.0 ,我给你列个表格你就明白了

qMIME
1.0text/html
1.0application/xhtml+xml
0.9application/xml
0.8* / *

也就是说,这是一个放置顺序,权重高的在前,低的在后,application/xml;q=0.9 是不可分割的整体。

Accept-Charset

Accept-charset 属性规定服务器处理表单数据所接受的字符编码;Accept-charset 属性允许你指定一系列字符集,服务器必须支持这些字符集,从而得以正确解释表单中的数据。

Accept-Charset 没有对应的标头,服务器会把这个值放在 Content-Type中用 charset=xxx来表示,

例如,浏览器请求 GBK 或 UTF-8 的字符集,然后服务器返回的是 UTF-8 编码,就是下面这样

Accept-Charset: gbk, utf-8
Content-Type: text/html; charset=utf-8
Accept-Language

首部字段 Accept-Language 用来告知服务器用户代理能够处理的自然语言集(指中文或英文等),以及自然语言集的相对优先级。可一次指定多种自然语言集。和 Accept 首部字段一样,按权重值 q= 来表示相对优先级。

Accept-Language: en-US,en;q=0.5
Accept-Encoding

表示 HTTP 标头会标明客户端希望服务端返回的内容编码,这通常是一种压缩算法。Accept-Encoding 也是属于内容协商 的一部分,使用并通过客户端选择 Content-Encoding 内容进行返回。

即使客户端和服务器都能够支持相同的压缩算法,服务器也可能选择不压缩并返回,这种情况可能是由于这两种情况造成的:

  • 要发送的数据已经被压缩了一次,第二次压缩并不会导致发送的数据更小
  • 服务器过载,无法承受压缩带来的性能开销,通常,如果服务器使用 CPU 超过 80% ,Microsoft 则建议不要使用压缩

下面是 Accept-Encoding 的使用方式

Accept-Encoding: gzip
Accept-Encoding: compress
Accept-Encoding: deflate
Accept-Encoding: br
Accept-Encoding: identity
Accept-Encoding: *
Accept-Encoding: deflate, gzip;q=1.0, *;q=0.5

上面的几种表述方式就已经把 Accept-Encoding 的属性列全了

  • gzip: 由文件压缩程序 gzip 生成的编码格式,使用 Lempel-Ziv编码(LZ77)和32位CRC的压缩格式,感兴趣的同学可以读一下 (https://en.wikipedia.org/wiki/LZ77_and_LZ78#LZ77)

  • compress: 使用Lempel-Ziv-Welch(LZW)算法的压缩格式,有兴趣的同学可以读 (https://en.wikipedia.org/wiki/LZW)

  • deflate: 使用 zlib 结构和 deflate 压缩算法的压缩格式,参考 (https://en.wikipedia.org/wiki/Zlib) 和 (https://en.wikipedia.org/wiki/DEFLATE)

  • br: 使用 Brotli 算法的压缩格式,参考 (https://en.wikipedia.org/wiki/Brotli)

  • 不执行压缩或不会变化的默认编码格式

  • * : 匹配标头中未列出的任何内容编码,如果没有列出 Accept-Encoding ,这就是默认值,并不意味着支

    持任何算法,只是表示没有偏好

  • ;q= 采用权重 q 值来表示相对优先级,这点与首部字段 Accept 相同。

Content-Type

Content-Type 实体标头用于指示资源的 MIME 类型。作为响应,Content-Type 标头告诉客户端返回的内容的内容类型实际上是什么。Content-type 有两种值 : MIME 类型和字符集编码,例如

Content-Type: text/html; charset=UTF-8

在某些情况下,浏览器将执行 MIME 嗅探,并且不一定遵循此标头的值;为防止此行为,可以将标头 X-Content-Type-Options 设置为 nosniff。

Content-Encoding

Content-Encoding 实体标头用于压缩媒体类型,它让客户端知道如何进行解码操作,从而使客户端获得 Content-Type 标头引用的 MIME 类型。表示如下

Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate
Content-Encoding: identity
Content-Encoding: br
Content-Encoding: gzip, identity
Content-Encoding: deflate, gzip

Content-Language

Content-Language 实体标头用于描述面向受众的语言,以便使用户根据用户自己的首选语言进行区分。例如

Content-Language: de-DE
Content-Language: en-US
Content-Language: de-DE, en-CA

下面根据内容协商对应的请求/响应标头,我列了一张图供你参考,注意其中 Accept-Charset 没有对应的 Content-Charset ,而是通过 Content-Type 来表示。

在这里插入图片描述

HTTP 认证

HTTP 提供了用于访问控制和身份认证的功能,下面就对 HTTP 的权限和认证功能进行介绍

通用 HTTP 认证框架

RFC 7235 定义了 HTTP 身份认证框架,服务器可以根据其文档的定义来检查客户端请求。客户端也可以根据其文档定义来提供身份验证信息。

请求/响应的工作流程如下:服务器以401(未授权) 的状态响应客户端告诉客户端服务器需要认证信息,客户端提供至少一个 www-Authenticate 的响应标头进行授权信息的认证。想要通过服务器进行身份认证的客户端可以在请求标头字段中添加认证标头进行身份认证,一般的认证过程如下

在这里插入图片描述

首先客户端发起一个 HTTP 请求,不带有任何认证标头,服务器对此 HTTP 请求作出响应,发现此 HTTP 信息未带有认证凭据,服务器通过 www-Authenticate标头返回 401 告诉客户端此请求未通过认证。然后客户端进行用户认证,认证完毕后重新发起 HTTP 请求,这次 HTTP 请求带有用户认证凭据(注意,整个身份认证的过程必须通过 HTTPS 连接保证安全),到达服务器后服务器会检查认证信息,如果不符合服务器认证信息,会返回 403 Forbidden 表示用户认证失败,如果满足认证信息,则返回 200 OK

我们知道,客户端和服务器之间的 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

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

在这里插入图片描述

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

禁止访问

如果代理服务器收到的有效凭据不足以获取对给定资源的访问权限,则服务器应使用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 标头代表不缓存过期资源。

在这里插入图片描述

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

Cache-Control: no-cache

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

Cache-Control: max-age=0
私有缓存

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

在这里插入图片描述

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

Cache-Control: private
共享缓存

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

Cache-Control: public

在这里插入图片描述

缓存控制

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

下面是一个缓存验证图

在这里插入图片描述

什么是新鲜的数据

一旦资源存储在缓存中,理论上就可以永远被缓存使用。但是不管是浏览器缓存还是代理缓存,其存储空间是有限的,所以缓存会定期进行清除,这个过程叫做 缓存回收(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 取消此限制。

跨域的特点

  • 下面是跨域问题的例子,看看你是否清楚什么是跨域了
(1) http://example.com/app1/index.html
(2) http://example.com/app2/index.html

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

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

  • 那么下面这两个是否具有跨域问题呢?
http://Example.com:80
http://example.com

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

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

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

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

  • 下面这两个 URL 地址是否具有跨域问题?
http://example.com/app1
https://example.com/app2

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

  • 再看下面这三个 URL 是否具有跨域问题
http://example.com
http://www.example.com
http://myapp.example.com

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

  • 下面这两个 URL 是否具有跨域问题
http://example.com
http://example.com:8080

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

同源策略

处于安全的因素,浏览器限制了从脚本发起跨域的 HTTP 请求。 XMLHttpRequest 和其他 Fetch 接口 会遵循 同源策略(same-origin policy)。也就是说使用这些 API 的应用程序想要请求相同的资源,那么他们应该具有相同的来源,除非来自其他来源的响应包括正确的 CORS 标头也可以。

同源策略是一种很重要的安全策略,它限制了从一个来源加载的文档或脚本如何与另一个来源的资源进行交互。 它有助于隔离潜在的恶意文档,减少可能的攻击媒介。

我们上面提到,如果两个 URL 具有相同的协议、主机和端口号(如果指定)的话,那么两个 URL 具有相同的来源。下面有一些实例,你判断一下是不是具有相同的来源

目标来源 http://store.company.com/dir/page.html

URLOutcomeReason
http://store.company.com/dir2/other.html相同来源只有path不同
http://store.company.com/dir/inner/another.html相同来源只有path不同
https://store.company.com/page.html不同来源协议不通
http://store.company.com:81/dir/page.html不同来源默认端口不同
http://news.company.com/dir/page.html不同来源主机不同

现在我带你认识了两遍不同的源,现在你应该知道如何区分两个 URL 是否属于同一来源了吧!

好,你现在知道了什么是跨域问题,现在我要问你,哪些请求会产生跨域请求呢?这是我们下面要讨论的问题

跨域请求

跨域请求可能会从下面这几种请求中发出:

  1. 调用 XMLHttpRequest 或者 Fetch api。

XMLHttpRequest 是什么?(我是后端程序员,前端不太懂,简单解释下,如果解释的不好,还请前端大佬们不要胖揍我)

所有的现代浏览器都有一个内置的 XMLHttpReqeust 对象,这个对象可以用于从服务器请求数据。

XMLHttpReqeust 对于开发人员来说很重要,XMLHttpReqeust 对象可以用来做下面这些事情

  • 更新网页无需重新刷新页面
  • 页面加载后从服务器请求数据
  • 页面加载后从服务端获取数据
  • 在后台将数据发送到服务器

使用 XMLHttpRequest(XHR) 对象与服务器进行交互,你可以从 URL 检索数据从而不必刷新整个页面,这使网页可以更新页面的一部分,而不会中断用户的操作。XMLHttpRequest 在 AJAX 异步编程中使用很广泛。

再来说一下 Fetch API 是什么,Fetch 提供了请求和响应对象(以及其他网络请求)的通用定义。它还提供了相关概念的定义,例如 CORS 和 HTTP Origin 头语义,并在其他地方取代了它们各自的定义。

  1. Web 字体(用于 CSS 中@ font-face中的跨域字体使用),以便服务器可以部署 TrueType 字体,这些字体只能由允许跨站点加载和使用的网站使用。
  2. WebGL 纹理
  3. 使用 drawImage() 绘制到画布上的图像/视频帧
  4. 图片的 CSS 形状

跨域功能概述

跨域资源共享标准通过添加新的 HTTP 标头来工作,这些标头允许服务器描述允许哪些来源从 Web 浏览器读取信息。另外,对于可能导致服务器数据产生副作用的 HTTP 请求方法(尤其是 GET 或者具有某些 MIME 类型 POST 方法以外 HTTP 方法),该规范要求浏览器预检请求,使用 HTTP OPTIONS 请求方法从服务器请求受支持的方法,然后在服务器批准后发送实际请求。服务器还可以通知客户端是否应与请求一起发送凭据(例如 Cookies 和 HTTP 身份验证)。

注意:CORS 故障会导致错误,但是出于安全原因,该错误的详细信息不适用于 JavaScript。 所有代码都知道发生了错误。 确定具体出问题的唯一方法是查看浏览器的控制台以获取详细信息。

访问控制

下面我会和大家探讨三种方案,这些方案都演示了跨域资源共享的工作方式。所有这些示例都使用XMLHttpRequest,它可以在任何支持的浏览器中发出跨站点请求。

简单请求

一些请求不会触发 CORS预检 (关于预检我们后面再介绍)。简单请求是满足一下所有条件的请求

  • 允许以下的方法:GETHEADPOST

  • 除了由用户代理自动设置的标头(例如 Connection、User-Agent 或者在 Fetch 规范中定义为禁止标头名称的其他标头)外,唯一允许手动设置的标头是那些 Fetch 规范将其定义为 CORS安全列出的请求标头 ,它们是:

    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type(下面会介绍)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Content-Type 标头的唯一允许的值是

    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • 没有在请求中使用的任何 XMLHttpRequestUpload 对象上注册事件侦听器;这些可以使用XMLHttpRequest.upload 属性进行访问。

  • 请求中未使用 ReadableStream对象。

    例如,假定 web 内容 https://foo.example 想要获取 https://bar.other 域的资源,那么 JavaScript 中的代码可能会像下面这样写

    const xhr = new XMLHttpRequest();
    const url = 'https://bar.other/resources/public-data/';xhr.open('GET', url);
    xhr.onreadystatechange = someHandler;
    xhr.send(); 
    

这使用 CORS 标头来处理特权,从而在客户端和服务器之间执行某种转换。

在这里插入图片描述

让我们看看在这种情况下浏览器将发送到服务器的内容,并让我们看看服务器如何响应:

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example

注意请求的标头 Origin ,它表明调用来自于 https://foo.example。让我们看看服务器是如何响应的

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml[…XML Data…]

服务端发送 Access-Control-Allow-Origin 作为响应。使用 Origin 标头和 Access-Control-Allow-Origin 展示了最简单的访问控制协议。在这个事例中,服务端使用 Access-Control-Allow-Origin 作为响应,也就说明该资源可以被任何域访问。

如果位于https://bar.other的资源所有者希望将对资源的访问限制为仅来自https://foo.example的请求,他们应该发送如下响应

Access-Control-Allow-Origin: https://foo.example

现在除了 https://foo.example 之外的任何域都无法以跨域方式访问到 https://bar.other 的资源。

预检请求

和上面探讨的简单请求不同,预检请求首先通过 OPTIONS 方法向另一个域上的资源发送 HTTP 请求,用来确定实际请求是否可以安全的发送。跨站点这样被预检,因为它们可能会影响用户数据。

下面是一个预检事例

const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://bar.other/resources/post-here/');
xhr.setRequestHeader('X-PINGOTHER', 'pingpong');
xhr.setRequestHeader('Content-Type', 'application/xml');
xhr.onreadystatechange = handler;
xhr.send('<person><name>Arun</name></person>'); 

上面的事例创建了一个 XML 请求体用来和 POST 请求一起发送。此外,设置了非标准请求头 X-PINGOTHER ,这个标头不是 HTTP/1.1 的一部分,但通常对 Web 程序很有用。由于请求的 Content-Type 使用 application/xml,并且设置了自定义标头,因此该请求被预检。如下图所示

在这里插入图片描述

如下所述,实际的 POST 请求不包含 Access-Control-Request- * 标头;只有 OPTIONS 请求才需要它们。

下面我们来看一下完整的客户端/服务器交互,首先是预检请求/响应

OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive

上面的1 -11 行代表预检请求,预检请求使用 OPYIIONS 方法,浏览器根据上面的 JavaScript 代码段所使用的请求参数确定是否需要发送此请求,以便服务器可以响应是否可以使用实际请求参数发送请求。OPTIONS 是一种 HTTP / 1.1方法,用于确定来自服务器的更多信息,并且是一种安全的方法,这意味着它不能用于更改资源。请注意,与 OPTIONS 请求一起,还发送了另外两个请求标头(分别是第9行和第10行)

Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

Access-Control-Request-Method 标头作为预检请求的一部分通知服务器,当发送实际请求时,将使用POST 请求方法发送该请求。

Access-Control-Request-Headers 标头通知服务器,当发送请求时,它将与X-PINGOTHER 和 Content-Type 自定义标头一起发送。服务器可以确定这种情况下是否接受请求。

下面的 1 - 11行是服务器发回的响应,表示POST 请求和 X-PINGOTHER 是可以接受的,我们着重看一下下面这几行

Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400

服务器完成响应表明源 http://foo.example 是可以接受的 URL,能够允许 POST、GET、OPTIONS 进行请求,允许自定义标头 X-PINGOTHER, Content-Type。最后,Access-Control-Max-Age 以秒为单位给出一个值,这个值表示对预检请求的响应可以缓存多长时间,在此期间内无需发送其他预检请求。

完成预检请求后,将发送实际请求:

POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: https://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: https://foo.example
Pragma: no-cache
Cache-Control: no-cache<person><name>Arun</name></person>
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain[Some GZIP'd payload]

正式响应中很多标头我们在之前的文章已经探讨过了,本篇不再做详细的介绍,读者可以参考
你还在为 HTTP 的这些概念头疼吗? 查阅

带凭证的请求

XMLHttpRequest 或 Fetch 和 CORS 最有趣的功能就是能够发出知道 HTTP Cookie 和 HTTP 身份验证的 凭证 请求。默认情况下,在跨站点 XMLHttpRequest 或 Fetch 调用中,浏览器将不发送凭据。调用 XMLHttpRequest对象或 Request 构造函数时必须设置一个特定的标志。

在下面这个例子中,最初从 http://foo.example 加载的内容对设置了 Cookies 的 http://bar.other 上的资源进行了简单的 GET 请求, foo.example 上可能的代码如下

const invocation = new XMLHttpRequest();
const url = 'http://bar.other/resources/credentialed-content/';function callOtherDomain() {if (invocation) {invocation.open('GET', url, true);invocation.withCredentials = true;invocation.onreadystatechange = handler;invocation.send(); }
}

第7行显示 XMLHttpRequest 上的标志,必须设置该标志才能使用 Cookie 进行调用。默认情况下,调用是不在使用 Cookie 的情况下进行的。由于这是一个简单的 GET 请求,因此不会进行预检,但是浏览器将拒绝任何没有 Access-Control-Allow-Credentials 的响应:标头为true,指的是响应不会返回 web 页面的内容。

上面的请求用下图可以表示

在这里插入图片描述

这是客户端和服务器之间的示例交换:

GET /resources/access-control-with-credentials/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Referer: http://foo.example/examples/credential.html
Origin: http://foo.example
Cookie: pageAccess=2
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:34:52 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain[text/plain payload]

上面第10行包含指向http://bar.other 上的内容 Cookie,但是如果 bar.other 没有以 Access-Control-Allow-Credentials:true 响应(下面第五行),响应将被忽略,并且不能使用网站返回的内容。

请求凭证和通配符

当回应凭证请求时,服务器必须在 Access-Control-Allow-Credentials 中指定一个来源,而不能直接写* 通配符

因为上面示例代码中的请求标头包含 Cookie 标头,如果 Access-Control-Allow-Credentials 中是指定的通配符 * 的话,请求会失败。

注意上面示例中的 Set-Cookie 响应标头还设置了另外一个值,如果发生故障,将引发异常(取决于所使用的API)。

###HTTP 响应标头

下面会列出一些服务器跨域共享规范定义的 HTTP 标头,上面简单概述了一下,现在一起来认识一下,主要会介绍下面这些

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Credentials
  • Access-Control-Allow-Headers
  • Access-Control-Allow-Methods
  • Access-Control-Expose-Headers
  • Access-Control-Max-Age
  • Access-Control-Request-Headers
  • Access-Control-Request-Method
  • Origin
Access-Control-Allow-Origin

Access-Control-Allow-Origin 是 HTTP 响应标头,指示响应是否能够和给定的源共享资源。Access-Control-Allow-Origin 指定单个资源会告诉浏览器允许指定来源访问资源。对于没有凭据的请求 *通配符,告诉浏览器允许任何源访问资源。

例如,如果要允许源 https://mozilla.org 的代码访问资源,可以使用如下的指定方式

Access-Control-Allow-Origin: https://mozilla.org
Vary: Origin

如果服务器指定单个来源而不是*通配符,则服务器还应在 Vary 响应标头中包含该来源。

Access-Control-Allow-Credentials

Access-Control-Allow-Credentials 是 HTTP 的响应标头,这个标头告诉浏览器,当包含凭证请求(Request.credentials)时是否将响应公开给前端 JavaScript 代码。

这时候你会问到 Request.credentials 是什么玩意?不要着急,来给你看一下,首先来看 Request 是什么玩意,

实际上,Request 是 Fetch API 的一类接口代表着资源请求。一般创建 Request 对象有两种方式

  • 使用 Request() 构造函数创建一个 Request 对象
  • 还可以通过 FetchEvent.request api 操作来创建

再来说下 Request.credentials 是什么意思,Request 接口的凭据只读属性指示在跨域请求的情况下,用户代理是否应从其他域发送 cookie。(其他 Request 对象的方法详见 https://developer.mozilla.org/en-US/docs/Web/API/Request)

当发送的是凭证模式的请求包含 (Request.credentials)时,如果 Access-Control-Allow-Credentials 值为 true,浏览器将仅向前端 JavaScript 代码公开响应。

Access-Control-Allow-Credentials: true

凭证一般包括 cookie、认证头和 TLS 客户端证书

当用作对预检请求响应的一部分时,这表明是否可以使用凭据发出实际请求。注意简单的 GET 请求不会进行预检。

可以参考一个实际的例子 https://www.jianshu.com/p/ea485e5665b3

Access-Control-Allow-Headers

Access-Control-Allow-Headers 是一个响应标头,这个标头用来响应预检请求,它发出实际请求时可以使用哪些HTTP标头。

示例

  • 自定义标头

这是 Access-Control-Allow-Headers 标头的示例。它表明除了像 CROS 安全列出的请求标头外,对服务器的 CROS 请求还支持名为 X-Custom-Header 的自定义标头。

Access-Control-Allow-Headers: X-Custom-Header
  • 多个标头

这个例子展示了 Access-Control-Allow-Headers 如何使用多个标头

Access-Control-Allow-Headers: X-Custom-Header, Upgrade-Insecure-Requests
  • 绕过其他限制

尽管始终允许使用 CORS 安全列出的请求标头,并且通常不需要在 Access-Control-Allow-Headers 中列出这些标头,但是无论如何列出它们都将绕开适用的其他限制。

Access-Control-Allow-Headers: Accept

这里你可能会有疑问,哪些是 CORS 列出的安全标头?(别嫌累,就是这么麻烦)

有下面这些 Accep、Accept-Language、Content-Language、Content-Type ,当且仅当包含这些标头时,无需在 CORS 上下文中发送预检请求。

Access-Control-Allow-Methods

Access-Control-Allow-Methods 也是响应标头,它指定了哪些访问资源的方法可以使用预检请求。例如

Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Methods: *
Access-Control-Expose-Headers

Access-Control-Expose-Headers 响应标头表明哪些标头可以作为响应的一部分公开。默认情况下,仅公开6个CORS安全列出的响应标头,分别是

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

如果希望客户端能够访问其他标头,则必须使用 Access-Control-Expose-Headers 标头列出它们。下面是示例

要公开非 CORS 安全列出的请求标头,可以像如下这样指定

Access-Control-Expose-Headers: Content-Length

要另外公开自定义标头,例如 X-Kuma-Revision,可以指定多个标头,并用逗号分隔

Access-Control-Expose-Headers: Content-Length, X-Kuma-Revision

在不是凭证请求中,你还可以使用通配符

Access-Control-Expose-Headers: *

但是,这不会通配 Authorization 标头,因此如果需要公开它,则需要明确列出

Access-Control-Expose-Headers: *, Authorization
Access-Control-Max-Age

Access-Control-Max-Age 响应头表示预检请求的结果可以缓存多长时间,例如

Access-Control-Max-Age: 600 

表示预检请求可以缓存10分钟

Access-Control-Request-Headers

浏览器在发出预检请求时使用 Access-Control-Request-Headers 请求标头,使服务器知道在发出实际请求时客户端可能发送的 HTTP 标头。

Access-Control-Request-Headers: X-PINGOTHER, Content-Type

####Access-Control-Request-Method

同样的,Access-Control-Request-Method 响应标头告诉服务器发出预检请求时将使用那种 HTTP 方法。此标头是必需的,因为预检请求始终是 OPTIONS,并且使用的方法与实际请求不同。

Access-Control-Request-Method: POST
Origin

Origin 请求标头表明匹配的来源,它不包含任何信息,仅仅包含服务器名称,它与 CORS 请求以及 POST 请求一起发送,它类似于 Referer 标头,但与此标头不同,它没有公开整个路径。例如

Origin: https://developer.mozilla.org

HTTP 条件请求

HTTP 具有条件请求的概念,通过比较资源更新生成的值与验证器的值进行比较,来确定资源是否进行过更新。这样的请求对于验证缓存的内容、条件请求、验证资源的完整性来说非常重要。

原则

HTTP 条件请求是根据特定标头的值执行不同的请求,这些标头定义了一个前提条件,如果前提条件匹配或不匹配,则请求的结果将有所不同。

  • 对于 安全 的方法,像是 GET、用于请求文档的资源,仅当条件请求的条件满足时发回文档资源,所以,这种方式可以节约带宽。

什么是安全的方法,对于 HTTP 来说,安全的方法是不会改变服务器状态的方法,换句话说,如果方法只是只读操作,那么它肯定是安全的方法,比如说 GET 请求,它肯定是安全的方法,因为它只是请求资源。几种常见的方法肯定是安全的,它们是 GET、HEAD和 OPTIONS。所有安全的方法都是幂等的(这他妈幂等又是啥意思?)但不是所有幂等的方法都是安全的,例如 PUT 和 DELETE 都是幂等的,但不安全。

幂等性:如果相同的客户端发起一次或者多次 HTTP 请求会得到相同的结果,则说明 HTTP 是幂等的。(我们这次不深究幂等性)

  • 对于 非安全 的方法,像是 PUT,只有原始文档与服务器上存储的资源相同时,才可以使用条件请求来传输文档。(PUT 方法通常用来传输文件,就像 FTP 协议的文件上传一样)

验证

所有的条件请求都会尝试检查服务器上存储的资源是否与某个特定版本的资源相匹配。为了满足这种情况,条件请求需要指示资源的版本。由于无法和整个文件逐个字符进行比较,因此需要把整个文件描绘成一个值,然后把此值和服务器上的资源进行比较,这种方式称为比较器,比较器有两个条件

  • 文档的最后修改日期
  • 一个不透明的字符串,用于唯一标识每个版本,称为实体标签或 Etag

比较两个资源是否时相同的版本有些复杂,根据上下文,有两种相等性检查

  • 当期望的是字节对字节进行比较时,例如在恢复下载时,使用强 Etag 进行验证
  • 当用户代理需要比较两个资源是否具有相同的内容时,使用若 Etag 进行验证

HTTP 协议默认使用 强验证,它指定何时进行弱验证

强验证

强验证保证的是字节 级别的验证,严格的验证非常严格,可能在服务器级别难以保证,但是它能够保证任何时候都不会丢失数据,但这种验证丢失性能。

要使用 Last-Modified 很难实现强验证,通常,这是通过使用带有资源的 MD5 哈希值的 Etag 来完成的。

弱验证

弱验证不同于强验证,因为如果内容相等,它将认为文档的两个版本相同,例如,一个页面与另一个页面的不同之处仅在于页脚的日期不同,因此该页面被认为与其他页面相同。而使用强验证时则被认为这两个版本是不同的。构建一个若验证的 Etag 系统可能会非常复杂,因为这需要了解每个页面元素的重要性,但是对于优化缓存性能非常有用。

下面介绍一下 Etag 如何实现强弱验证。

Etag 响应头是特定版本的标识,它能够使缓存变得更高效并能够节省带宽,因为如果缓存内容未发生变更,Web 服务器则不需要重新发送完整的响应。除此之外,Etag 能够防止资源同时更新互相覆盖。

在这里插入图片描述

如果给定 URL 上的资源发生变更,必须生成一个新的 Etag 值,通过比较它们可以确定资源的两个表示形式是否相同。

Etag 值有两种,一种是强 Etag,一种是弱 Etag;

  • 强 Etag 值,无论实体发生多么细微的变化都会改变其值,一般的表示如下
Etag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
  • 弱 Etag 值,弱 Etag 值只用于提示资源是否相同。只有资源发生了根本改变,产生差异时才会改变 Etag 值。这时,会在字段值最开始处附加 W/。
Etag: W/"0815"

下面就来具体探讨一下条件请求的标头和 Etag 的关系

条件请求

条件请求主要包含的标头如下

  • If-Match
  • If-None-Match
  • If-Modified-Since
  • If-Unmodified-Since
  • If-Range
If-Match

对于 GETPOST 方法,服务器仅在与列出的 Etag(响应标头) 之一匹配时才返回请求的资源。这里又多了一个新词 Etag,我们稍后再说 Etag 的用法。对于像是 PUT 和其他非安全的方法,在这种情况下,它仅仅将上传资源。

下面是两种常见的案例

  • 对于 GETPOST 方法,会结合使用 Range 标头,它可以确保新发送请求的范围与上一个请求的资源相同,如果不匹配的话,会返回 416 响应。
  • 对于其他方法,特别是 PUT 方法,If-Match 可以防止丢失更新,服务器会比对 If-Match 的字段值和资源的 Etag 值,仅当两者一致时,才会执行请求。反之,则返回状态码 412 Precondition Failed 的响应。例如
If-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d"
If-Match: *
If-None-Match

条件请求,它与 If-Match 的作用相反,仅当 If-None-Match 的字段值与 Etag 值不一致时,可处理该请求。对于GETHEAD ,仅当服务器没有与给定资源匹配的 Etag 时,服务器将返回 200 OK作为响应。对于其他方法,仅当最终现有资源的 Etag 与列出的任何值都不匹配时,才会处理请求。

GETPOST 发送的 If-None-MatchEtag 匹配时,服务器会返回 304

If-None-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d"
If-None-Match: W/"67ab43", "54ed21", "7892dd"
If-None-Match: *
If-Modified-Since

If-Modified-Since 是 HTTP 条件请求的一部分,只有在给定日期之后,服务端修改了请求所需要的资源,才会返回 200 OK 的响应。如果在给定日期之后,服务端没有修改内容,响应会返回 304 并且不带任何响应体。If-Modified-Since 只能使用 GETHEAD 请求。

If-Modified-Since 与 If-None-Match 结合使用时,它将被忽略,除非服务器不支持 If-None-Match。一般表示如下

If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT 

注意:这是格林威治标准时间。 HTTP 日期始终以格林尼治标准时间表示,而不是本地时间。

If-Range

If-Range 也是条件请求,如果满足条件(If-Range 的值和 Etag 值或者更新的日期时间一致),则会发出范围请求,否则将会返回全部资源。它的一般表示如下

If-Range: Wed, 21 Oct 2015 07:28:00 GMT 
If-Range: bfc13a64729c4290ef5b2c2730249c88ca92d82d
If-Unmodified-Since

If-Unmodified-Since HTTP 请求标头也是一个条件请求,服务器只有在给定日期之后没有对其进行修改时,服务器才返回请求资源。如果在指定日期时间后发生了更新,则以状态码 412 Precondition Failed 作为响应返回。

If-Unmodified-Since: Wed, 21 Oct 2015 07:28:00 GMT 

条件请求示例

缓存更新

条件请求最常见的示例就是更新缓存,如果缓存是空或没有缓存,则以200 OK的状态发送回请求的资源。如下图所示

客户端第一次发送请求没有,缓存为空并且没有条件请求,服务器在收到客户端请求后,设置验证器 Last-ModifiedEtag 标签,并把这两个标签随着响应一起发送回客户端。

下一次客户端再发送相同的请求后,会直接从缓存中提取,只要缓存没有过期,就不会有任何新的请求到达服务器重新下载资源。但是,一旦缓存过期,客户端不会直接使用缓存的值,而是发出条件请求。 验证器的值用作 If-Modified-Since If-Match标头的参数。

缓存过期后客户端重新发起请求,服务器收到请求后发现如果资源没有更改,服务器会发回 304 Not Modified响应,这使缓存再次刷新,并让客户端使用缓存的资源。 尽管有一个响应/请求往返消耗一些资源,但是这比再次通过有线传输整个资源更有效。

如果资源已经发生更改,则服务器仅使用新版本的资源返回 200 OK 响应,就像没有条件请求,并且客户端会重新使用新的资源,从这个角度来讲,缓存是条件请求的前置条件

断点续传

HTTP 可以支持文件的部分下载,通过保留已获得的信息,此功能允许恢复先前的操作,从而节省带宽和时间。

支持断点续传的服务器通过发送 Accept-Ranges 标头广播此消息,一旦发生这种情况,客户端可以通过发送缺少范围的 Ranges 标头来恢复下载

在这里插入图片描述

这里你可能有疑问 RangesContent-Range是什么,来解释一下

Range

Range HTTP 请求标头指示服务器应返回文档指定部分的资源,可以一次请求一个 Range 来返回多个部分,服务器会将这些资源返回各个文档中。如果服务器成功返回,那么将返回 206 响应;如果 Range 范围无效,服务器返回416 Range Not Satisfiable错误;服务器还可以忽略 Range 标头,并且返回 200 作为响应。

Range: bytes=200-1000, 2000-6576, 19000-

还有一种表示是

Range: bytes=0-499, -500 

它们分别表示请求前500个字节和最后500个字节,如果范围重叠,则服务器可能会拒绝该请求。

Content-Range

HTTP 的 Content-Range 响应标头是针对范围请求而设定的,返回响应时使用首部字段 Content-Range,能够告知客户端响应实体的哪部分是符合客户端请求的,字段以字节为单位。它的一般表示如下

Content-Range: bytes 200-1000/67589 

上段代码表示从所有 67589 个字节中返回 200-1000 个字节的内容

那么上面的 Content-Range你也应该知道是什么意思了

断点续传的原理比较简单,但是这种方式存在潜在的问题:如果在两次下载资源的期间进行了资源更新,那么获得的范围将对应于资源的两个不同版本,并且最终文档将被破坏。

为了阻止这种情况的出现,就会使用条件请求。对于范围来说,有两种方法可以做到这一点。一种方法是使用 If-Modified-SinceIf-Match,如果前提条件失败,服务器将返回错误;然后客户端从头开始重新下载。

在这里插入图片描述

即使此方法有效,当文档资源发生改变时,它也会添加额外的 响应/请求 交换。这会降低性能,并且 HTTP 具有特定的标头来避免这种情况 If-Range

在这里插入图片描述

该解决方案效率更高,但灵活性稍差一些,因为在这种情况下只能使用一个 Etag。

通过乐观锁避免丢失更新

Web 应用程序中最普遍的操作是资源更新。这在任何文件系统或应用程序中都很常见,但是任何允许存储远程资源的应用程序都需要这种机制。

使用 put 方法,你可以实现这一点,客户端首先读取原始文件对其进行修改,然后把它们发送到服务器。

上面这种请求响应存在问题,一旦考虑到并发性,事情就会变得不准确。当客户端在本地修改资源打算重新发送之前,第二个客户端可以获取相同的资源并对资源进行修改操作,这样就会造成问题。当它们重新发送请求到服务器时,第一个客户端所做的修改将被第二次客户端的修改所覆盖,因为第二次客户端修改并不知道第一次客户端正在修改。资源提交并更新的一方不会传达给另外一方,所以要保留哪个客户的更改,将随着他们提交的速度而变化; 这取决于客户端,服务器的性能,甚至取决于人工在客户端编辑文档的性能。 例如下面这个流程

如果没有两个用户同时操作服务器,也就不存在这个问题。但是,现实情况是不可能只有单个用户出现的,所以为了规避或者避免这个问题,我们希望客户端资源在更新时进行提示或者修改被拒绝时收到通知。

条件请求允许实现乐观锁算法。这个概念是允许所有的客户端获取资源的副本,然后让他们在本地修改资源,并成功通过允许第一个客户端提交更新来控制并发,基于此服务端的后面版本的更新都将被拒绝。

这是使用 If-MatchIf-Unmodified-Since标头实现的。如果 Etag 与原始文件不匹配,或者自获取以来已对文件进行了修改,则更改为拒绝更新,并显示412 Precondition Failed错误。

HTTP Cookies

HTTP 协议中的 Cookie 包括 Web Cookie浏览器 Cookie,它是服务器发送到 Web 浏览器的一小块数据。服务器发送到浏览器的 Cookie,浏览器会进行存储,并与下一个请求一起发送到服务器。通常,它用于判断两个请求是否来自于同一个浏览器,例如用户保持登录状态。

HTTP Cookie 机制是 HTTP 协议无状态的一种补充和改良

Cookie 主要用于下面三个目的

  • 会话管理

登陆、购物车、游戏得分或者服务器应该记住的其他内容

  • 个性化

用户偏好、主题或者其他设置

  • 追踪

记录和分析用户行为

Cookie 曾经用于一般的客户端存储。虽然这是合法的,因为它们是在客户端上存储数据的唯一方法,但如今建议使用现代存储 API。Cookie 随每个请求一起发送,因此它们可能会降低性能(尤其是对于移动数据连接而言)。客户端存储的现代 API 是 Web 存储 API(localStorage 和 sessionStorage)和 IndexedDB。

创建 Cookie

当接收到客户端发出的 HTTP 请求时,服务器可以发送带有响应的 Set-Cookie 标头,Cookie 通常由浏览器存储,然后将 Cookie 与 HTTP 标头一同向服务器发出请求。可以指定到期日期或持续时间,之后将不再发送Cookie。此外,可以设置对特定域和路径的限制,从而限制 cookie 的发送位置。

Set-Cookie 和 Cookie 标头

Set-Cookie HTTP 响应标头将 cookie 从服务器发送到用户代理。下面是一个发送 Cookie 的例子

HTTP/2.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry[page content]

此标头告诉客户端存储 Cookie

现在,随着对服务器的每个新请求,浏览器将使用 Cookie 头将所有以前存储的 cookie 发送回服务器。

GET /sample_page.html HTTP/2.0
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

Cookie 主要分为三类,它们是 会话Cookie永久CookieCookie的 Secure 和 HttpOnly 标记,下面依次来介绍一下

会话 Cookies

上面的示例创建的是会话 Cookie ,会话 Cookie 有个特征,客户端关闭时 Cookie 会删除,因为它没有指定Expires 或 Max-Age 指令。 这两个指令你看到这里应该比较熟悉了。

但是,Web 浏览器可能会使用会话还原,这会使大多数会话 Cookie 保持永久状态,就像从未关闭过浏览器一样

永久性 Cookies

永久性 Cookie 不会在客户端关闭时过期,而是在特定日期(Expires)或特定时间长度(Max-Age)外过期。例如

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
Cookie的 Secure 和 HttpOnly 标记

安全的 Cookie 需要经过 HTTPS 协议通过加密的方式发送到服务器。即使是安全的,也不应该将敏感信息存储在cookie 中,因为它们本质上是不安全的,并且此标志不能提供真正的保护。

HttpOnly 的作用

  • 会话 cookie 中缺少 HttpOnly 属性会导致攻击者可以通过程序(JS脚本、Applet等)获取到用户的 cookie 信息,造成用户cookie 信息泄露,增加攻击者的跨站脚本攻击威胁。

  • HttpOnly 是微软对 cookie 做的扩展,该值指定 cookie 是否可通过客户端脚本访问。

  • 如果在 Cookie 中没有设置 HttpOnly 属性为 true,可能导致 Cookie 被窃取。窃取的 Cookie 可以包含标识站点用户的敏感信息,如 ASP.NET 会话 ID 或 Forms 身份验证票证,攻击者可以重播窃取的 Cookie,以便伪装成用户或获取敏感信息,进行跨站脚本攻击等。

Cookie 的作用域

DomainPath 标识定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。

Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前主机(不包含子域名)。如果指定了Domain,则一般包含子域名。

例如,如果设置 Domain=mozilla.org,则 Cookie 也包含在子域名中(如developer.mozilla.org)。

例如,设置 Path=/docs,则以下地址都会匹配:

  • /docs
  • /docs/Web/
  • /docs/Web/HTTP

相关文章:

HTTP 黑科技

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…...

【Vite + Vue + Ts 项目三个 tsconfig 文件】

Vite Vue Ts 项目三个 tsconfig 文件 为什么 Vite Vue Ts 项目会有三个 tsconfig 文件&#xff1f;首先我们先了解什么是 tsconfig.json ? 为什么 Vite Vue Ts 项目会有三个 tsconfig 文件&#xff1f; 在使用 Vite 创建 vue-ts 模板的项目时&#xff0c;会发现除了 ts…...

Mac怎么彻底卸载软件,简单彻底的卸载方式

一个阳光明媚的下午&#xff0c;咖啡厅里&#xff0c;珂珂正在和他的好友帅帅讨论如何优化他们的Mac电脑&#xff0c;特别是谈到Mac怎么彻底卸载软件的时候&#xff0c;帅帅就特别感同身受&#xff0c;因为他近期就遇到了这个的麻烦&#xff0c;并且找了很久才找到号的解决方案…...

15 刚体变换模块(rigid.rs)

rigid.rs是一个表示三维刚体变换&#xff08;Rigid Transformation&#xff09;的结构体定义&#xff0c;用于在计算机图形学、机器人学以及物理模拟等领域中表示物体在三维空间中的旋转和平移。在这个定义中&#xff0c;所有长度在变换后都保持不变&#xff0c;这是刚体变换的…...

springboot使用rabbitmq

使用springboot创建rabbitMQ的链接。 整个项目结构如下&#xff1a; 1.maven依赖 <dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>3.4.1</version> </dependency>application.y…...

【Linux】一文带你入门了解线程和虚拟地址空间中页表映射的秘密(内附手绘底层逻辑图 通俗易懂)

绪论​ 每日激励&#xff1a;“努力去做自己该做的&#xff0c;但是不要期待回报&#xff0c;不是付出了就会有回报的&#xff0c;做了就不要后悔&#xff0c;不做才后悔。—Jack” 绪论​&#xff1a; 本章是LInux中非常重要的线程部分&#xff0c;通过了解线程的基本概念&am…...

高并发、高可用的消息队列(MQ)设计与实战

目录 背景与历史消息队列的核心功能高并发、高可用的业务场景消息队列的实用性企业规模与消息队列的选择Java实战案例&#xff1a;基于RabbitMQ的高并发、高可用消息队列 6.1 环境准备6.2 RabbitMQ的安装与配置6.3 Java客户端集成6.4 生产者与消费者实现6.5 高并发处理6.6 高可…...

nginx 新手指南

文章来源&#xff1a;https://nginx.cadn.net.cn/beginners_guide.html 本指南对 nginx 进行了基本的介绍&#xff0c;并描述了一些 可以用它完成的简单任务。 假设 nginx 已经安装在阅读器的机器上。 如果不是&#xff0c;请参阅 安装 nginx 页面。 本指南介绍如何启动和停止…...

7-4 西安距离

小明来到了古都西安&#xff0c;想去参观大唐西市&#xff01; 西安的道路可以看做是与x轴或y轴垂直的直线&#xff0c;小明位于(a,b)&#xff0c;而目的地位于(c,d)&#xff0c;问最少几步可以到达。 输入格式: 一行中四个整数&#xff0c;a,b,c,d&#xff0c;表示坐标为(a…...

VScode+Latex (Recipe terminated with fatal error: spawn xelatex ENOENT)

使用VSCode编辑出现Recipe terminated with fatal error: spawn xelatex ENOENT问题咋办&#xff1f; 很好解决&#xff0c;大概率的原因是因为latex没有添加到系统环境变量中&#xff0c;所有设置的编译工具没有办法找到才出现的这种情况。 解决方法&#xff1a; winR 然后输…...

使用 Elastic Cloud Hosted 优化长期数据保留:确保政府合规性和效率

作者&#xff1a;来自 Elastic Jennie Davidowitz 在数字时代&#xff0c;州和地方政府越来越多地承担着管理大量数据的任务&#xff0c;同时确保遵守严格的监管要求。这些法规可能因司法管辖区而异&#xff0c;通常要求将数据保留较长时间 —— 有时从一年到七年不等。遵守刑事…...

51单片机 02 独立按键

一、独立按键控制LED亮灭 轻触按键&#xff1a;相当于是一种电子开关&#xff0c;按下时开关接通&#xff0c;松开时开关断开&#xff0c;实现原理是通过轻触按键内部的金属弹片受力弹动来实现接通和断开。 #include <STC89C5xRC.H> void main() { // P20xFE;while(1){…...

海外问卷调查渠道查,具体运营的秘密

相信只要持之以恒并逐渐掌握技巧&#xff0c;每一位调查人在踏上征徐之时都会非常顺利的。并在日后的职业生涯中拥有捉刀厮杀的基本技能&#xff01;本文会告诉你如何做好一个优秀的海外问卷调查人。 在市场经济高速发展的今天&#xff0c;众多的企业为了自身的生存和发展而在…...

Vue.js 的介绍与组件开发初步

Vue.js 的介绍与组件开发初步 Vue.js 的介绍与组件开发初步引言第一部分&#xff1a;Vue.js 基础入门1.1 什么是 Vue.js&#xff1f;1.2 搭建 Vue.js 开发环境安装 Node.js 和 npm安装 Vue CLI创建新项目运行示例 1.3 第一个 Vue.js 示例 第二部分&#xff1a;Vue.js 组件开发基…...

Shadow DOM举例

这东西具有隔离效果&#xff0c;对于一些插件需要append一些div倒是不错的选择 <!DOCTYPE html> <html lang"zh-CN"> <head> <meta charset"utf-8"> <title>演示例子</title> </head> <body> <style&g…...

kamailio-Core 说明书 版本:Kamailio SIP Server v6.0.x(稳定版)

Core 说明书 版本&#xff1a;Kamailio SIP Server v6.0.x&#xff08;稳定版&#xff09; 概述 本教程收集了 Kamailio 导出的函数和参数 core 添加到配置文件中。 注意&#xff1a;此页面上的参数不按字母顺序排列。 结构 kamailio.cfg 的结构可以看作是三个部分&#xff…...

PHP XML操作指南

PHP XML操作指南 引言 随着互联网的快速发展&#xff0c;数据交换和共享变得越来越重要。XML&#xff08;可扩展标记语言&#xff09;作为一种灵活的标记语言&#xff0c;被广泛应用于各种数据交换场景。PHP作为一种流行的服务器端脚本语言&#xff0c;具有强大的XML处理能力…...

一文了解DeepSeek

1. DeepSeek 的起源 创立时间&#xff1a;DeepSeek 于 2023 年由中国的梁文锋创立。 V3 模型训练成本&#xff1a;最终训练成本为 600 万美元。 开源&#xff1a;DeepSeek 提供开源版本。 流行度&#xff1a;DeepSeek R1 模型成为 Apple 应用商店中下载量最高的应用。 2. …...

三角形的最大周长(976)

976. 三角形的最大周长 - 力扣&#xff08;LeetCode&#xff09; 可以一起总结的题目&#xff1a;三数之和&#xff08;15&#xff09;-CSDN博客 官方解法&#xff1a; class Solution { public://官方解法int largestPerimeter(vector<int>& nums) {sort(nums.be…...

10:预处理

预处理 1、宏替换2、头文件包含3、条件编译4、typedef和#define的区别5、#define中的注意点5.1、使用do....while(0)5.2、#和##的含义 C语言编译器在编译程序之前&#xff0c;会先使用预处理器(预处理器)处理代码&#xff0c;代码经过预处理之后再送入编译器进行编译。预处理器…...

一文讲解HashMap线程安全相关问题(上)

HashMap不是线程安全的&#xff0c;主要有以下几个问题&#xff1a; ①、多线程下扩容会死循环。JDK1.7 中的 HashMap 使用的是头插法插入元素&#xff0c;在多线程的环境下&#xff0c;扩容的时候就有可能导致出现环形链表&#xff0c;造成死循环。 JDK 8 时已经修复了这个问…...

C++泛型编程指南04-(对默认调用参数的类型推断)

文章目录 问题描述解决方案示例代码 关键点解释进一步改进&#xff1a;结合概念约束 你提到的情况确实是一个常见的问题&#xff1a;在C中&#xff0c;类型推断不适用于默认调用参数。这意味着如果你希望函数模板能够通过默认参数来实例化&#xff0c;你需要为模板参数提供一个…...

Python爬虫:1药城店铺爬虫(完整代码)

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…...

浅谈知识蒸馏技术

最近爆火的DeepSeek 技术&#xff0c;将知识蒸馏技术运用推到我们面前。今天就简单介绍一下知识蒸馏技术并附上python示例代码。 知识蒸馏&#xff08;Knowledge Distillation&#xff09;是一种模型压缩技术&#xff0c;它的核心思想是将一个大型的、复杂的教师模型&#xff0…...

【人工智能】使用Python和Hugging Face构建情感分析应用:从模型训练到Web部署

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 情感分析是自然语言处理(NLP)中的重要任务,它通过分析文本来判断情绪或观点的倾向性。近年来,预训练语言模型如BERT、GPT等在情感分析任…...

【R语言】函数

一、函数格式 如下所示&#xff1a; hello&#xff1a;函数名&#xff1b;function&#xff1a;定义的R对象是函数而不是其它变量&#xff1b;()&#xff1a;函数的输入参数&#xff0c;可以为空&#xff0c;也可以包含参数&#xff1b;{}&#xff1a;函数体&#xff0c;如果…...

python leetcode 笔记

只为记录一些python相关的特殊写法 无穷大&#xff0c;无穷小&#xff0c;NAN float(inf), float(-inf), float(nan) 判断字符的类型 isdigit(x) isspace(x) 字符串拼接 /.join([a,b,c]) # a/b/c 格式转换&#xff0c;字符转整形 ord(a) # 97 chr(97) # a 进制转…...

基于SpringBoot的青年公寓服务平台的设计与实现(源码+SQL脚本+LW+部署讲解等)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...

深入剖析 HTML5 新特性:语义化标签和表单控件完全指南

系列文章目录 01-从零开始学 HTML&#xff1a;构建网页的基本框架与技巧 02-HTML常见文本标签解析&#xff1a;从基础到进阶的全面指南 03-HTML从入门到精通&#xff1a;链接与图像标签全解析 04-HTML 列表标签全解析&#xff1a;无序与有序列表的深度应用 05-HTML表格标签全面…...

kamailio的kamctl的使用

kamctl 是 Kamailio SIP 服务器的管理工具&#xff0c;用于执行各种管理任务&#xff0c;如启动、停止、重启 Kamailio 进程&#xff0c;管理用户、ACL、路由、信任的 IP 地址等。以下是对 kamctl 命令的解释及举例说明&#xff1a; 1. 启动、停止、重启 Kamailio start: 启动…...

[创业之路-270]:《向流程设计要效率》-2-企业流程架构模式 POS架构(规划、业务运营、支撑)、OES架构(业务运营、使能、支撑)

目录 一、POS架构 二、OES架构 三、POS架构与OES架构的差异 四、各自的典型示例 POS架构典型示例 OES架构典型示例 示例分析 五、各自的典型企业 POS架构典型企业 OES架构典型企业 分析 六、各自典型的流程 POS架构的典型流程 OES架构的典型流程 企业流程架构模式…...

【leetcode100】路径总和Ⅲ

1、题目描述 给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum &#xff0c;求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始&#xff0c;也不需要在叶子节点结束&#xff0c;但是路径方向必须是向下的&#xff08;只能从父节点…...

用结构加法3ax+1预测第4点的分布

有1个点在19*19的平面上在某种力的作用下运动&#xff0c;轨迹为 共移动了90步&#xff0c;按照&#xff08;0&#xff0c;1&#xff0c;2&#xff0c;3&#xff09;&#xff0c;&#xff08;1&#xff0c;2&#xff0c;3&#xff0c;4&#xff09;&#xff0c;…&#xff0c;&…...

CTF-web: Python YAML反序列化利用

PyYAML存在以下几个特殊标签,如果这些标签被不安全的解析,会造成解析漏洞 从 PyYaml 版本 6.0 开始&#xff0c;load 的默认加载器已切换到 SafeLoader&#xff0c;以降低远程代码执行的风险。更新后易受攻击的是 yaml.unsafe_load 和 yaml.load(input, Loaderyaml.UnsafeLoade…...

JDK-1.8.0_432安装(CentOS7)

目录 1、卸载系统自带JDK 2、下载安装包并解压 3、赋予可执行权限 4、设置环境变量 5、刷新环境变量 6、查看JDK版本 1、卸载系统自带JDK # 查询出自带的jdk rpm -qa | grep jdk rpm -qa | grep java # 将上述命令列出的包依次删除 rpm -e --nodeps xxxxxxx 2、下载…...

OpenGL学习笔记(五):Textures 纹理

文章目录 纹理坐标纹理环绕方式纹理过滤——处理纹理分辨率低的情况多级渐远纹理Mipmap——处理纹理分辨率高的情况加载与创建纹理 &#xff08; <stb_image.h> &#xff09;生成纹理应用纹理纹理单元练习1练习2练习3练习4 通过上一篇着色部分的学习&#xff0c;我们可以…...

【Pytorch和Keras】使用transformer库进行图像分类

目录 一、环境准备二、基于Pytorch的预训练模型1、准备数据集2、加载预训练模型3、 使用pytorch进行模型构建 三、基于keras的预训练模型四、模型测试五、参考 现在大多数的模型都会上传到huggface平台进行统一的管理&#xff0c;transformer库能关联到huggface中对应的模型&am…...

2025年Android开发趋势全景解读

文章目录 一、界面开发&#xff1a;从"手写代码"到"智能拼装"1.1 Jetpack Compose实战进化1.2 淘汰XML布局的三大信号 二、AI融合开发&#xff1a;无需炼丹的普惠智能2.1 设备端AI三大杀手级应用2.2 成本对比&#xff1a;设备端VS云端AI 三、跨平台演进&am…...

Python NumPy(12):NumPy 字节交换、NumPy 副本和视图、NumPy 矩阵库(Matrix)

1 NumPy 字节交换 在几乎所有的机器上&#xff0c;多字节对象都被存储为连续的字节序列。字节顺序&#xff0c;是跨越多字节的程序对象的存储规则。 大端模式&#xff1a;指数据的高字节保存在内存的低地址中&#xff0c;而数据的低字节保存在内存的高地址中&#xff0c;这样的…...

【Vaadin flow 实战】第5讲-使用常用UI组件绘制页面元素

vaadin flow官方提供的UI组件文档地址是 https://vaadin.com/docs/latest/components这里&#xff0c;我简单实战了官方提供的一些免费的UI组件&#xff0c;使用案例如下&#xff1a; Accordion 手风琴 Accordion 手风琴效果组件 Accordion 手风琴-测试案例代码 Slf4j PageT…...

第三篇:模型压缩与量化技术——DeepSeek如何在边缘侧突破“小而强”的算力困局

——从算法到芯片的全栈式优化实践 随着AI应用向移动终端与物联网设备渗透&#xff0c;模型轻量化成为行业核心挑战。DeepSeek通过自研的“算法-编译-硬件”协同优化体系&#xff0c;在保持模型性能的前提下&#xff0c;实现参数量与能耗的指数级压缩。本文从技术原理、工程实…...

搜索与图论复习2最短路

单源最短路---所有边权是正数(Dijkstra算法O(n^2)--稠密图(邻接矩阵)和堆优化的Dijkstra算法O(mlogn)--稀疏图(邻接表)) 或存在负边权(Bellman-ford贝尔曼福特算法O(nm)和SPFA一般O(m) 最坏O(nm) ) 多源最短路---Floyd算法O(n^3) 一、迪杰斯特拉算法(Dijkstra)&#xff1a;1…...

redis集群理论详解

一. Redis集群发展历程 本片文章只介绍集群理论知识&#xff0c;不包含Redis集群搭建教程 教程文章请点击docker搭建redis集群&#xff08;三主三从&#xff09; 阶段一&#xff1a;单机版Redis 优点&#xff1a; 简单&#xff1a;易于部署和使用&#xff0c;适合小型项目或初期…...

本地缓存~

前言 Caffeine是使用Java8对Guava缓存的重写版本&#xff0c;在Spring Boot 2.0中取而代之&#xff0c;基于LRU算法实现&#xff0c;支持多种缓存过期策略。 以下摘抄于https://github.com/ben-manes/caffeine/wiki/Benchmarks-zh-CN 基准测试通过使用Java microbenchmark ha…...

SpringBoot 整合 SpringMVC:SpringMVC的注解管理

分类&#xff1a; 中央转发器(DispatcherServlet)控制器视图解析器静态资源访问消息转化器格式化静态资源管理 中央转发器&#xff1a; 中央转发器被 SpringBoot 自动接管&#xff0c;不需要我们在 web.xml 中配置&#xff1a; <servlet><servlet-name>chapter2&l…...

YOLO11/ultralytics:环境搭建

前言 人工智能物体识别行业应该已经饱和了吧&#xff1f;或许现在并不是一个好的入行时候。 最近看到了各种各样相关的扩展应用&#xff0c;为了理解它&#xff0c;我不得不去尝试了解一下。 我选择了git里非常受欢迎的yolo系列&#xff0c;并尝试了最新版本YOLO11或者叫它ultr…...

扩散模型(三)

相关阅读&#xff1a; 扩散模型&#xff08;一&#xff09; 扩散模型&#xff08;二&#xff09; Latent Variable Space 潜在扩散模型&#xff08;LDM&#xff1b;龙巴赫、布拉特曼等人&#xff0c;2022 年&#xff09;在潜在空间而非像素空间中运行扩散过程&#xff0c;这…...

探索数学:从起源到未来的无尽旅程

数学的定义与本质 数学&#xff0c;这门古老而又充满魅力的学科&#xff0c;自人类文明诞生之初便如影随形。然而&#xff0c;要精准地定义数学并非易事&#xff0c;不同的学者从各自的视角出发&#xff0c;给出了多样的阐释。 亚里士多德将数学定义为 “数量科学”&#xff…...

OpenAI发布o3-mini:免费推理模型,DeepSeek引发的反思

引言 在人工智能领域&#xff0c;OpenAI再次引领潮流&#xff0c;推出了全新的推理模型系列——o3-mini。这一系列包括low、medium和high三个版本&#xff0c;旨在进一步推动低成本推理的发展。与此同时&#xff0c;OpenAI的CEO奥特曼也在Reddit的“有问必答”活动中罕见地公开…...

React中使用箭头函数定义事件处理程序

React中使用箭头函数定义事件处理程序 为什么使用箭头函数&#xff1f;1. 传递动态参数2. 避免闭包问题3. 确保每个方块的事件处理程序是独立的4. 代码可读性和维护性 示例代码总结 在React开发中&#xff0c;处理事件是一个常见的任务。特别是当我们需要传递动态参数时&#x…...