图解 HTTP 的缓存机制 | 实用 HTTP

一、序

Hi,大家好,我是承香墨影!

HTTP 协议在网络知识中占据了重要的地位,HTTP 协议最基础的就是请求和响应的报文头(Header),大多数 Http
协议的使用方式,都是依赖设置不同的 HTTP 请求/响应 的 Header 来实现的。

本系列《实用 HTTP》就抛开常规的 Header 讲解式的表述方式,从实际问题出发,来分析这些 Http
协议的使用方式,到底是为了解决什么问题?同时讲解它是如何设计的和它实现原理。

HTTP
协议是一种无状态的“松散协议”,它不会记录不同请求的状态,并且因为它本身包含了两端(客户端和服务端),根据请求和响应来区分,它大部分的内容都只是一个建议,其实双边是可以不遵守此建议的。例如:服务端说,这个数据缓存有一天的时效性,但是客户端可以说,我不听我不听,我就要每次去重新请求。

“这里写了建议零售价 2 元…”

“哦,不接受建议!”

说到缓存,本文就来说说 HTTP 缓存相关的内容。

二、HTTP缓存使用

2.1 为什么需要缓存

缓存说白了就是为了快,无论是从磁盘到内存还是从网络到本地,都是为了在下次实用此资源的时候,能够快速响应,避免多次的 I/O 操作。

通过网络获取资源,是一件耗时的操作,较大的资源还会需要客户端和服务端之间进行多次往返通信,这不但会增加客户端响应的时间,同时还会增加网络流量。

在 HTTP 协议中,天然就有对缓存的支持,浏览器和 App 使用的开源网络库中,都是利用 HTTP 缓存来实现对资源的缓存。

浏览器是天然支持 HTTP 缓存,开源库则需要进行一些例如存规则和缓存的资源存放路径之类的简单设定。

2.2 设计一个缓存策略

那如果让我们来设计缓存的策略,首先有两个重要的指标需要考虑。

1.缓存失效

既然缓存主要是针对数据的复用,那我们就需要有一个条件来判定当前缓存的数据,是否依然有效。

总是不能一次缓存,终身使用吧,我们还需要在缓存失效之后,重新获取新的数据并进行缓存。这个前提就是,缓存都需要有一个失效的策略。

2.减少读取

虽然缓存会有失效策略,但是这只是客户端单方面认为失效,此时应该再去服务端重新获取一遍数据。

可有些情况下,其实资源可能依然有效,并没有发生变动。那就需要有一个策略,让服务端通知客户端,当前缓存依然有效,可以继续使用。这样在减少传输流量之外,也可以加快相应时间,提高效率。

这就是一个好的缓存策略必须要考虑的地方,实际上 HTTP 缓存,也是这样设计的。

2.3 HTTP 缓存

HTTP 缓存主要是通过请求和响应报文头中的对应 Header 信息,来控制缓存的策略。

这里主要涉及两个 Header:

  • Cache-Control:设定缓存策略,是否使用缓存,超时时间是多少。

  • ETag:当前返回数据的验证令牌,可能是 Hash 值也可能是其他指纹,主要用于在下次请求的时候携带上,让服务端依此判断当前数据是否有更改。

服务端在返回响应数据的时候,会在报文头中,增加用于描述当前响应的内容类型、数据长度、缓存策略(Cache-Control)、验证令牌(ETag)等信息。

例如上图就表示了一次请求响应的事务,大概客户端请求一个文件的时候,服务端返回了一个 200 的状态码,表示响应正常,响应的数据长度为 1024
个字节,建议客户端将此资源缓存最多 120 秒,并且提供了一个指纹令牌(“cxmyDev123”),用来作为当前数据的唯一标识。

2.4 ETag 数据令牌

Cache-Control 中设定的 max-age 很好理解,就是设定缓存超时的时间,HTTP 缓存是限定一个超时的秒数,来确定缓存失效的时间。

上古时期还会使用 expires 来决定超时的日期,但是已经被废弃了,如果和 Cache-Control 同时存在,以 Cache-Control
为准。

在此时间间隔范围内,客户端不会再向服务端发送新的请求。当资源距离上一次缓存的时间间隔,大于 120 秒后,客户端才会再次向服务端发送请求。

假如没有数据令牌的情况下,大概步骤应该是这样的:

  1. 客户端会首先找到本地缓存,然后发现它已经失效,无法再次使用。

  2. 客户端再次向服务端发出新的请求,并获取完整的数据再次进行缓存。之后再刷新该缓存的超时时间。

但是这是一件效率非常低的事情,服务端并无法确定所持有的源资源什么时候会失效,所以提供的 max-age
值,只是一个参考值,是需要取平衡的,太短会导致请求频繁,太长又会导致无法及时刷新客户端资源。而此时再次请求的时候,是存在一定的概率,客户端缓存的数据和服务端上持有的数据是一致的,我们就不需要再次对此数据资源进行二次缓存,直接使用客户端之前缓存的数据即可,同时还需要刷新缓存超时时间。

这正是数据验证令牌(ETag)想要解决的问题,服务端生成并返回的这个数据指纹令牌,通常就是返回数据的 Hash
值或者其他数据指纹,客户端无需关心它的生成规则,只需要知道它是当前数据的一个唯一标识。

客户端需要在下次请求时将其通过 If-None-Match
这个请求报文头,将此验证令牌发送至服务端,如果数据令牌指纹和服务端当前的数据一致,则标识资源未发生新的变化。就会返回一个 304
的状态码,表示可以继续使用客户端本地缓存的数据,并刷新超时时间。注意当响应码为 304 的时候,它是不包含数据内容的。

通常此缓存操作对我们都是透明的,它是浏览器和开源网络库的基本实现,我们无需自己去判断 max-age 和 ETag
的值,这一步我们只需要确定服务端对此有支持即可。

这里只是提到了 If-None-Match,它标识比较 ETag 是否不一致,除此之外,还有一些其他的相关报文头,例如 If-
Match,有兴趣可以查阅相关资料。

2.5 Cache-Control

前面举的例子中,我们只为 Cache-Control 设定了一个 max-age,但是其实还有一些更丰富的配置。

从缓存性能最优化的角度来看,最佳的缓存是无需与服务端通信的缓存,可以通过缓存来消灭网络延迟以及数据请求,从而来提高用户的体验。

Cache-Control 是在 HTTP/1.1 中被定义的,它可以用于取代之前的缓存策略,现在所有的浏览器都支持 Cache-Control
,它已经成为一种通用的标准。

Cache-Control 还有一些更灵活的配置,用来对缓存做一些更细致的操作。

  1. “no-cache” 和 “no-store”

这两个参数都表示每一次请求,都需要真实的发送一个网络请求。

它们之间的区别在于,“no-cache”并不是真的不缓存数据,它只是要求每次都确认资源是否过期,也就是它会利用数据令牌 ETag
来一定程度的减小传输的流量。

而 “no-store” 完全是要求客户端,每次都重新请求数据并下载最新的数据,不做任何缓存处理。这种不缓存的策略,也包括中间连接的代理、网关
等中间传输的通道,也一并不对数据进行缓存,每次都从源服务器上获取数据。

  1. “public” 和 “private”

“public” 是一种默认的策略,表示当前缓存是开放的,任何请求响应的中间环节,都可以对其进行缓存,如果我们不显式指定,则当前为 “public” 缓存。

与之相对的
“private”,则表示当前响应是针对单个用户的,并非通用数据,因此不建议任何中间缓存对其进行缓存。例如:浏览器就是一个比较私人的缓存源,它会缓存
“private” 的缓存,而 CDN 则不会。

三、最佳的缓存策略树

前面提到,缓存的核心目的就是为了快,能让下次使用的时候快速复用。所以在理想情况下,我们应该将响应数据尽可能多的缓存,尽可能的缓存足够长的时间,并且为每个资源提供单独的数据验证令牌,以便在时间过期之后快速校验。

但是任何事情都是要取其平衡点的,不存在什么最佳缓存策略,并非所有响应资源都需要加缓存,这就需要根据业务场景来设定。

这里给出一个增加 HTTP 缓存的通用策略树,你在对响应增加缓存的时候,可以参考它来执行。

正常情况下,我们针对不同的响应属性,会对它设置不同的缓存策略,下面根据场景,举几个例子。

3.1 用户相关的数据

和单个用户紧密相关的数据,通常我们是不建议使用缓存的,但是依然存在几个等级。

  1. 严格不使用缓存

Cache-Control:no-store

  1. 允许客户终端缓存,但是每次使用都需要确认

Cache-Control:no-cache

ETag:”cxmyDev1234”

  1. 允许客户终端短时间缓存

Cache-Control:private max-age=600

ETag:”cxmyDev1234”

3.2 通用数据

一些通用响应资源,更新的频率非常的低,我们可以根据需要调整 max-age 的大小即可。

Cache-Control:max-age=86400

ETag:”cxmyDev1234”

四、废弃和更新缓存的响应

缓存的策略,一旦确定并下发到客户端,服务端就失去了对齐的控制权。也就是说,如果我们设定了 max-
age,在此资源有效期超时之前,哪怕服务端的源资源已经被替换修改,我们也没有一个合适的时机去通知客户端更新新的响应数据。

那么有没有什么好的策略去标记资源废弃?同时又能友好的利用缓存策略。

在互联网上,所有服务上的资源,都有一个对应的 URL(统一资源定位符),它可以明确说明如何从一个精确且固定的位置获取资源。而 HTTP 缓存,也是依赖于
URL 的,注意 URL 是大小写敏感的,同一个 URL 表示同一个请求响应,依此来判断缓存和后续缓存的复用。

所以我们是可以在 URL 上做文章的。

4.1 浏览器的废弃策略

前面提到,浏览器是天然支持 HTTP 缓存的,对于浏览器来说,它所面对的就是一个个 HTML 页面,页面内会包含一些
CSS、Image、JavaScript、JSON 资源和数据。

针对不同的资源和数据,我们可以在其 URL 上,增加数据令牌指纹,当资源变动的时候,同时也去刷新改指纹令牌。

到这里就很好理解了:

  • HTML 页面,使用 no-cache,强制每次都向源服务器确认数据。

  • CSS文件通常变动的频率非常低,所以可以允许中间层缓存,并且缓存时间为一年不过期。

  • JavaScript内有业务逻辑,可以设定为只允许客户终端缓存。

  • getUserInfo,是为个人用户数据相关,这里推荐可缓存,但是需要每次向服务器重新确认。

4.2 App 接口的缓存策略

在 App 中使用的接口,其实和网页又不一样,HTML 网页的结构类似一个树形结构,先通过获取 .html
文件获取其内所有资源的表,然后依次根据缓存策略进行访问。

但是在 App 中,和服务器的交互都是通过数据接口来实现的,就不存在最开始获取一个类似 HTML
文件这样的树形接口,每个接口都是一个个“孤岛”,可以单独存在。我们就无法提前知道某个接口的响应数据已经过期,同时也无法修改 URL 上携带的数据指纹令牌。

但是其实我们是可以通过 App 和设备的一些固有信息,作为 URL 的参数传递,以此来刷新数据。

例如这里 /app/main 获取主页的数据,这里将当前 App 的版本号当参数拼接在 URL
的后面,以此方式来强制不同的版本,刷新不同的数据。避免刚升级上来的 App,还在使用旧版本的数据。

这个例子中,版本号只是其中一个维度,如果有必要,还可以传递其他维度的信息,例如当前网络状态,当前用户 id 等等。

五、小结

到这里我们基本上把 HTTP 的缓存所有相关的内容都讲了一遍,这里简单总结一下。

  • HTTP 缓存依赖 URL 做唯一标识,不同的 URL 使用不同的缓存。

  • Cache-Control 可以控制缓存策略,共有或者私有、缓存超时时长等。

  • 通过 ETag 来标记数据指纹令牌,以此来确定响应数据是否更新。

  • 应该为每个响应资源提供对应的缓存策略。

  • 如果需要废弃之前的缓存,可以利用修改请求 URL 的方式,将数据指纹令牌追加在 URL 之后,以此来更新数据。

HTTP 内容编码,也就这 2 点需要知道 | 实用 HTTP

Hi,大家好,我是承香墨影!

HTTP 协议在网络知识中占据了重要的地位,HTTP 协议最基础的就是请求和响应的报文,而报文又是由报文头(Header)和实体组成。大多数 Http
协议的使用方式,都是依赖设置不同的 HTTP 请求/响应 的 Header 来实现的。

本系列《实用 HTTP》就抛开常规的 Header 讲解式的表述方式,从实际问题出发,来分析这些 HTTP
协议的使用方式,到底是为了解决什么问题?同时讲解它是如何设计的和它实现原理。

HTTP
协议是一种无状态的“松散协议”,它不会记录不同请求的状态,并且因为它本身包含了两端(客户端和服务端),根据请求和响应来区分,它大部分的内容都只是一个建议,其实双边是可以不遵守此建议的。

“这里写了建议零售价 2 元…”

“哦,不接受建议!”

在上一篇文章中,聊到了 HTTP
的缓存机制
,其实缓存的主要起因就是为了减少网络请求次数,来达到快速响应的目的。而除了减少网络请求之外,其实我们还可以通过对实体内容,进行编码压缩的方式,减少传输的内容大小,从而加快响应的速度。

本文就就继续来聊聊 HTTP 的实体内容压缩编码机制。

二、HTTP 的内容编码

2.1 为什么要对内容进行编码?

编码的目的就是为了压缩报文实体内容的大小,而通过压缩服务器响应报文传输的内容实体,在一定程度上就可以加快响应的速度。

毕竟传输一个 10kb 的内容,会比传输一个 100kb 的内容快很多。这就是需要使用内容编码进行压缩的原因。

2.2 压缩编码

说到压缩编码,就先简单聊聊压缩算法,对于压缩算法而言,分为两类:

  • 无损压缩算法

  • 有损压缩算法

从名称上就可以理解,无损压缩意味着它是可以被还原的,通常被应用在文本,而有损压缩会对原始数据进行修改,以加大压缩率的目的,对文件进行有损失的压缩,这是一种不可逆的操作,通常一些对质量要求不高的图片和视频上,虽然压缩以后可能会导致文件模糊,但是勉强还可以看。

而在 HTTP
协议中,通常我们只会对文本内容,进行压缩编码。一个主要的原因在于,压缩本身是会消耗服务器资源的,而文件比多媒体文件轻便了很多。并且多媒体文件多数情况下,本身就已经是高度压缩的二进制格式,再次进行压缩的意义也不大。

2.3 设计一个“压缩协议”

前面提到,HTTP 协议是一种松散的 “协商协议”,需要客户端和服务端双端配合,才可以生效。而压缩算法有很多种,到底应该选择哪一种,也是需要双方协商的。

如果我们尝试设计一下这个 HTTP 的 “压缩协议”,主要需要关注这两点。

1.通知服务端,客户端支持的压缩算法

一个 HTTP 事务,总是由客户端发起请求,而服务端将响应返回。那么客户端就要在发起请求的时候,率先告知服务端,当前客户端支持的压缩算法。

通常客户端会支持多种压缩算法,为了让服务端有选择的空间,应该允许传递多个支持的压缩算法。既然有多选的空间,那么就一定要有优先级的概念。

类似于我们在市场上交易,我接受人民币、美元、比特币的交易,但是因为我使用人民币更方便,所以我需要指明交易方,如果方便的话最好通过人民币交易。

2.服务端选择支持的压缩算法压缩内容

服务端接受到客户端的请求后,辨识出客户端支持的压缩算法,现在当前环境最优的一种压缩算法对响应内容体进行压缩,然后将压缩后的内容返回。

为了让客户端接收到响应后,能明确知道服务端使用的压缩算法,还需要在响应中明确指明,当前的响应实体的数据使用的压缩算法(当然也可以不压缩)。

2.4 HTTP 的“压缩协议”

前面我们自己设计的两个条件,都是基于 HTTP 报文中的报文头来实现的。接下来我们看看 HTTP 协议中,是如何设计“压缩协议”的。

1.请求头中的 Accept-Encoding

客户端为了告知服务端当前支持的压缩编码,可以在请求头中,增加 Accept-Encoding
这个头部字段,用来指定当前客户端支持的压缩编码,如果有多个可以使用逗号 , 进行分割。

为了满足优先级,其实是可以通过 , 分割的顺序来指定的。HTTP 协议中,还可以使用 Q 值来说明编码的优先级,Q 值的取值范围是 0.0 ~ 1.0。0.0 表示客户端不想接受此编码,而 1.0 则表示希望使用此编码,不过通常我们不需要明确的指定它,大家了解一下即可。

2.响应头中的 content-encoding

服务端为了在响应报文里体现当前对内容压缩使用的编码格式,会在响应头中使用 Content-Encoding 标记,它是一个明确值,所以只可能有一个。

编码的目的就是为了压缩,所以当服务端选择压缩内容实体的时候,同时还会修改 Content-Length 来明确表示当前实体被编码压缩后的长度。

发两张压缩前和压缩后的流程图,就清晰了。

压缩前:

压缩后:

三、HTTP 的编码类型

3.2 HTTP 编码类型

HTTP
定义了一些标准的内容编码类型,并且可以扩展更多的编码类型。由互联网号码分配机构(IANA)对各种编码进行标准化,它给每个内容编码算法分配一个唯一的代号。

Content-Encoding 就是用这些标准化的代号来说明编码使用的算法。

比较常用的算法有:

  • gzip:表明实体采用 GNU zip 编码。

  • compress:表明实体采用 Unix 的文件压缩程序。

  • deflate:表明使用是用 zlib 的格式压缩的。

  • br:表明实体使用 Brotli 算法的压缩格式。

  • identity:表明没有对实体进行编码,为默认值。

在这些算法中,除了 identity 之外,都是无损压缩,他们都是需要可还原成原始的文本内容的。gzip 通常是效率最高的,使用最广泛的。

但是 gzip 对媒体文件的压缩效果相对较差,本身 JPG/PNG 这类文件已经是一种高度压缩的二进制文件,开启 gzip 效果甚微还会浪费大量 CPU
资源。

浏览器的默认实现中,这些压缩编码通常只会作用在文本内容上,就是 Content-Type 为 text/Xxx
的请求上,而对于一些媒体文件,则不会使用这种方式对其进行压缩。

3.2 GZIP

既然 gzip 是 HTTP 的内容编码中,比较常用的一种编码方式,这里抛砖引玉,简单介绍一些 gzip,其他编码方式,有兴趣的可以自行查阅相关资料。

gzip 编码是采用的 GNU Zip 编码,是一种无损的压缩算法,用于减少传输报文实体的大小,它是可逆的压缩算法,不会导致信息损失。

gzip 的压缩效率相对较高,并且使用也是最为广泛的,我们在工作中如果不特殊说明,说到的 HTTP 压缩,通常就是指的 gzip。

gzip
的原理,简单来说,就是会去扫描整个文本的字符串,找到一样的字符串,就只保留一个并分配一个标识,然后将其他相同的字符串使用这个标识替换,使整个文件变小。在还原的时候,只需要将每个标识代表的字符串,替换还原,就可以还原成最初的内容实体。

这种压缩算法,非常适用于现在的互联网产品,HTML、CSS、JavaScript 以及 Json 中,都包含了大量重复的字符串,所以在这里使用 gzip
是非常合适的。

gzip 具体能压缩多少,完全取决于压缩的实体内容,内容文本中,包含越多相同的字符串,压缩率就越高,相反则越低。在理想状态下,gzip 的压缩率能高达
70%。

四、内容编码的完整过程

到此我们就算了解清楚 HTTP 对内容编码的完整流程了。大致流程如下图。

再总结几个关键点:

  1. 请求头中,通过 Accept-Encoding 来指定客户端支持的内容编码格式。

  2. 服务端选择一个支持的内容编码去压缩原始响应内容实体。

  3. 修改响应头,增加 Content-Encoding 用于指定使用的编码方式,并且修改 Content-Length 来表明压缩后的内容大小。

  4. 内容压缩的算法有很多,但是 gzip 是最常用的。

  5. 内容压缩算法,都是基于无损压缩,最终都需要在客户端将内容还原。

五、小结

一个报文通常会包含报文头部和报文实体,而本文介绍的 HTTP 压缩编码,主要是针对报文实体内容中,文本内容的压缩编码,并为涉及到报文头部的压缩。主要是因为在
HTTP/1中,报文头部始终是以 ASCII 文本传输,没有经过任何压缩,而在 HTTP/2 中才对其实现了解决方案,所以 HTTP
的编码压缩只是针对报文实体的,这句话并不全对,这个有机会以后再说。

除了内容编码之外,HTTP 还有传输编码,这个同样也是有机会再说。

在本文中,说明了 HTTP 对报文实体内容的压缩策略和方法,希望对你有帮助。


详解 Cookie 纪要

#

从事 Web
开发已有近17个月;在学以致用的工作学习里,对于不怎么使用的部分,多少有些雾里探花的窘迫感~差不多是了解一二,然而又非真切的明晰;这就使得再用的时候,总要去再搜索一番;如此颇为难受,倒不如总结纪录下来,一方面加深认知,也易便于查阅;对于某相关技术,不断学习、运用、总结、更新,积淀过后也能对别人给予帮助。写博,真是一件值得去做的事。

背景

在HTTP协议的定义中,采用了一种机制来记录客户端和服务器端交互的信息,这种机制被称为cookie,cookie规范定义了服务器和客户端交互信息的格式、生存期、使用范围、安全性。

在JavaScript中可以通过 document.cookie 来读取或设置这些信息。由于 cookie
多用在客户端和服务端之间进行通信,所以除了JavaScript以外,服务端的语言(如PHP)也可以存取 cookie。

Cookie详解

Cookie在远程浏览器端存储数据并以此跟踪和识别用户的机制。从实现上说,Cookie是存储在客户端上的一小段数据,浏览器(即客户端)通过HTTP协议和服务器端进行Cookie交互。

Cooke独立于语言存在,严格地说,Cookie并不是由PHP、Java等语言实现的,而是由这些语言对Cookie进行间接操作,即发送HTTP指令,浏览器收到指令便操作Cookie并返回给服务器。因此,Cookie是由浏览器实现和管理的。举例说,PHP并没有真正设置过Cookie,只是发出指令让浏览器来做这件事。PHP中可以使用setcookie()
或 setrawcookie()
函数设置Cookie。setcookie()最后一个参数HttpOnly设置了后,JavaScript就无法读取到这个Cookie。

设置Cookie时需注意:①函数有返回值,false失败,true成功,成功仅供参考,不代表客户端一定能接收到;②PHP设置的Cookie不能立即生效,要等下一个页面才能看到(Cookie从服务器传给浏览器,下个页面浏览器才能把设置的Cookie传回给服务器);如果是JavaScript设置的,是立即生效的;③Cookie没有显示的删除函数,可以设置expire过期时间,自动触发浏览器的删除机制。

Cookie是HTTP头的一部分,即现发送或请求Cookie,才是data域;setcookie()等函数必须在数据之前调用,这和header()
函数是相同的。不过也可以使用输出缓冲函数延迟脚本的输出,知道设置好所有Cookie和其他HTTP标头。

Cookie通常用来存储一些不是很敏感的信息,或者进行登录控制,也可用来记住用户名、记住免密码登录、防止刷票等。每个域名下允许的Cookie是有限制的,根据浏览器这个限制也不同。Cookie不是越多越好,它会增加宽带,增加流量消耗,所以不要滥用Cookie;不要把Cookie当作客户端的存储器来用。一个域名的每个Cookie限制以4千字节(KB)键值对的形式存储。

还有一种Cookie是Flash创建的,成为Flash Shard Object,又称Flash
Cookie,即使清空浏览器所有隐私数据,这类顽固的Cookie还会存在硬盘上,因为它只受Flash管理,很多网站采用这种技术识别用户。

Cookie跨域,主要是为了统一应用平台,实现单点登录;需使用P3P协议(Platform for Privacy
Preferences),通过P3P使用户自己可以指定浏览器的隐私策略,达到存储第三方Cookie的目的,只需要在响应用户请求时,在HTTP的头信息中增加关于P3P的配置信息就可以了。Cookie跨域涉及两个不同的应用,习惯上称为第一方和第三方。第三方通常是来自别人的广告、或Iframe别的网站的URL,这些第三方网站可能使用的Cookie。

Cookie格式

Cookie中保存的信息都是文本信息,在客户端和服务器端交互过程中,cookie信息被附加在HTTP消息头中传递,cookie的信息由键/值对组成。下面是一个HTTP头中cookie的例子:

Set-Cookie: key = value; Path=/

Cookie中存放的信息包含cookie本身属性和用户自定义属性,一个cookie只能包含一个自定义键/值对。Cookie本身属性有”Comment”
、”Domain”、”Max-Age”、”Path”、”Secure”、”Version”。

Comment 属性是cookie的产生着对该cookie的描述;

Domain 属性定义可访问该cookie的域名,对一些大的网站,如果希望cookie可以在子网站中共享,可以使用该属性。例如设置Domain为
.bigsite.com
,则sub1.bigsite.com和sub2.bigsite.com都可以访问已保存在客户端的cookie,这时还需要将Path设置为/。

Max-Age 属性定义cookie的有效时间,用秒计数,当超过有效期后,cookie的信息不会从客户端附加在HTTP消息头中发送到服务端。

Path
属性定义网站上可以访问cookie的页面的路径,缺省状态下Path为产生cookie时的路径,此时cookie可以被该路径以及其子路径下的页面访问;可以将Path设置为/,使cookie可以被网站下所有页面访问。

Secure
属性值定义cookie的安全性,当该值为true时必须是HTTPS状态下cookie才从客户端附加在HTTP消息中发送到服务端,在HTTP时cookie是不发送的;Secure为false时则可在HTTP状态下传递cookie,Secure缺省为false。

Version 属性定义cookie的版本,由cookie的创建者定义。

Cookie的创建

Cookie可以在服务器端创建,然后cookie信息附加在HTTP消息头中传到客户端,如果cookie定义了有效期,则本保存在客户端本地磁盘。保存cookie的文件是一个文本文件,因此不用担心此文件中的内容会被执行而破坏客户的机器。支持Web端开发的语言都有创建cookie的方法或函数,以及设置cookie属性和添加自定义属性的方法或函数,最后是将cookie附加到返回客户端的HTTP消息头中。

创建cookie时如果不指定生存有效时间,则cookie只在浏览器关闭前有效,cookie会在服务器端和客户端传输,但是不会保存在客户机的磁盘上,打开新的浏览器将不能获得原先创建的cookie信息。

Cookie信息保存在本地时会保存到当前登录用户专门目录下,保存的cookie文件名中会包含创建cookie所在页面网站的域名,当浏览器再次连接该网站时,会从本机cookie存放目录下选出该网站的有效cookie,将保存在其中的信息附加在HTTP消息头中发送到服务器端,服务器端程序就可根据上次保存在cookie的信息为访问客户提供“记忆”或个性化服务。

Cookie除了可以在服务器端创建外,也可以在客户端的浏览器中用客户端脚本(如javascript)创建。客户端创建的cookie的性质和服务器端创建的cookie一样,可以保存在本地,也可以被传送到服务器端被服务器程序读取。

Cookie 基础知识

  1. cookie 是有大小限制的,大多数浏览器支持最大为 4096 字节的 Cookie(具体会有所差异,可以使用这个好用的工具:http://browsercookielimits.squawky.net/ 进行测试);如果 cookie 字符串的长度超过最大限制,则该属性将返回空字符串。
  2. 由于 cookie 最终都是以文件形式存放在客户端计算机中,所以查看和修改 cookie 都是很方便的,这就是为什么常说 cookie 不能存放重要信息的原因。
  3. 每个 cookie 的格式都是这样的:cookieName = Vaue;名称和值都必须是合法的标示符。
  4. cookie 是存在 有效期的。在默认情况下,一个 cookie 的生命周期就是在浏览器关闭的时候结束。如果想要 cookie 能在浏览器关掉之后还可以使用,就必须要为该 cookie 设置有效期,也就是 cookie 的失效日期。
  5. alert(typeof document.cookie)结果是 string.
  6. cookie 有域和路径这个概念。域就是domain的概念,因为浏览器是个注意安全的环境,所以不同的域之间是不能互相访问 cookie 的(当然可以通过特殊设置的达到 cookie 跨域访问)。路径就是routing的概念,一个网页所创建的 cookie 只能被与这个网页在同一目录或子目录下得所有网页访问,而不能被其他目录下得网页访问(这句话有点绕,一会看个例子就好理解了)。
  7. 其实创建cookie的方式和定义变量的方式有些相似,都需要使用 cookie 名称和 cookie 值。同个网站可以创建多个 cookie ,而多个 cookie 可以存放在同一个cookie 文件中。
  8. cookie 存在两种类型:①:你浏览的当前网站本身设置的 cookie ②来自在网页上嵌入广告或图片等其他域来源的 第三方 cookie (网站可通过使用这些 cookie 跟踪你的使用信息)
  9. cookie 有两种清除方式:①:通过浏览器工具清除 cookie (有第三方的工具,浏览器自身也有这种功能) ②通过设置 cookie 的有效期来清除 cookie.注:删除 cookie 有时可能导致某些网页无法正常运行。
  10. 浏览器可以通过设置来接受和拒绝访问 cookie。出于功能和性能的原因考虑,建议尽量降低 cookie 的使用数量,并且要尽量使用小 cookie。

Cookie的使用

从cookie的定义可以看到,cookie一般用于采用HTTP作为进行信息交换协议的客户端和服务器端用于记录需要持久化的信息。一般是由服务器端创建要记录的信息,然后传递到客户端,由客户端从HTTP消息中取出信息,保存在本机磁盘上。当客户端再次访问服务器端时,从本机磁盘上读出原来保存的信息,附加到HTTP消息中发送给服务器端,服务器端从HTTP消息中读取信息,根据实际应用的需求进行进一步的处理。

服务器端cookie的创建和再次读取功能通常由服务器端编程语言实现,客户端cookie的保存、读取一般由浏览器来提供,并且对cookie的安全性方面可以进行设置,如是否可以在本机保存cookie。

由于cookie信息以明文方式保存在文本文件中,对一些敏感信息如口令、银行帐号如果要保存在本地cookie文件中,最好采用加密形式。

与cookie类似的另一个概念是会话(Session),会话一般是记录客户端和服务器端从客户端浏览器连接上服务器端到关闭浏览器期间的持久信息。会话一般保存在内存中,不保存到磁盘上。会话可以通过cookie机制来实现,对于不支持cookie的客户端,会话可以采用URL重写方式来实现。可以将会话理解为内存中的cookie。

使用会话会对系统伸缩性造成负面影响,当服务器端要在很多台服务器上同步复制会话对象时,系统性能会受到较大伤害,尤其会话对象较大时。这种情况下可以采用cookie,将需要记录的信息保存在客户端,每次请求时发送到服务器端,服务器端不保留状态信息,避免在服务器端多台机器上复制会话而造成的性能下降。

Cookie 基本操作

对于 Cookie 得常用操作有,存取,读取,以及设置有效期;具体可以参照 JavaScript 操作 Cookie
一文;但,近期在前端编码方面,皆以Vue为冲锋利器,所以就有用到一款插件 vue-cookie,其代码仅30行,堪称精妙,读取操作如下:

set: function (name, value, days) {

var d = new Date;

d.setTime(d.getTime() + 2460601000days);

window.document.cookie = name + “=” + value + “;path=/;expires=” +
d.toGMTString();

},

get: function (name) {

var v = window.document.cookie.match(‘(^|;) ?’ + name + ‘=([^;]*)(;|$)’);
return v ? v[2] : null;

},

delete: function (name) { this.set(name, ‘’, -1);

}

cookie 域概念

路径能解决在同一个域下访问 cookie 的问题,咱们接着说 cookie 实现同域之间访问的问题。语法如下:

document.cookie = “name=value;path=path;domain=domain“

红色的domain就是设置的 cookie 域的值。例如 “www.qq.com” 与 “sports.qq.com”
公用一个关联的域名”qq.com”,我们如果想让”sports.qq.com” 下的cookie被 “www.qq.com”
访问,我们就需要用到cookie 的domain属性,并且需要把path属性设置为 “/“。例:

document.cookie = “username=Darren;path=/;domain=qq.com“

注:一定的是同域之间的访问,不能把domain的值设置成非主域的域名。

cookie 安全性

通常 cookie 信息都是使用HTTP连接传递数据,这种传递方式很容易被查看,在控制台下运行document.cookie,一目了然;所以 cookie
存储的信息容易被窃取。假如 cookie 中所传递的内容比较重要,那么就要求使用加密的数据传输。所以 cookie
的这个属性的名称是“secure”,默认的值为空。如果一个 cookie
的属性为secure,那么它与服务器之间就通过HTTPS或者其它安全协议传递数据。语法如下:

document.cookie = “username=Darren;secure”

把cookie设置为secure,只保证 cookie 与服务器之间的数据传输过程加密,而保存在本地的
cookie文件并不加密。如果想让本地cookie也加密,得自己加密数据。

注: 就算设置了secure 属性也并不代表他人不能看到你机器本地保存的 cookie 信息,所以说到底,别把重要信息放cookie就对了。

Session详解

Session即回话,指一种持续性的、双向的连接。Session与Cookie在本质上没有区别,都是针对HTTP协议的局限性而提出的一种保持客户端和服务器间保持会话连接状态的机制。Session也是一个通用的标准,但在不同的语言中实现有所不同。针对Web网站来说,Session指用户在浏览某个网站时,从进入网站到浏览器关闭这段时间内的会话。由此可知,Session实际上是一个特定的时间概念。

使用Session可以在网站的上下文不同页面间传递变量、用户身份认证、程序状态记录等。常见的形式就是配合Cookie使用,实现保存用户登录状态功能。和Cookie一样,session_start()
必须在程序最开始执行,前面不能有任何输出内容,否则会出现警告。PHP的Session默认通过文件的方式实现,即存储在服务器端的Session文件,每个Session一个文件。

Session通过一个称为PHPSESSID的Cookie和服务器联系。Session是通过sessionID判断客户端用户的,即Session文件的文件名。sessionID实际上是在客户端和服务端之间通过HTTP
Request 和 HTTP Response传来传去。sessionID按照一定的算法生成,必须包含在 HTTP Request
里面,保证唯一性和随机性,以确保Session的安全。如果没有设置 Session 的生成周期,
sessionID存储在内存中,关闭浏览器后该ID自动注销;重新请求该页面,会重新注册一个sessionID。如果客户端没有禁用Cookie,Cookie在启动Session回话的时候扮演的是存储sessionID
和 Session 生存期的角色。Session过期后,PHP会对其进行回收。

假设客户端禁用Cookie,可以通过URL或者隐藏表单传递sessionID;php.ini中把session.use_trans_sid
设成1,那么连接后就会自己加Session的ID。

Session以文件的形式存放在本地硬盘的一个目录中,当比较多时,磁盘读取文件就会比较慢,因此把Session分目录存放。

对于访问量大的站点,用默认的Session存储方式并不适合,较优的方法是用Data
Base存取Session。在大流量的网站中,Session入库存在效率不高、占据数据库connection资源等问题。针对这种情况,可以使用Memcached
、Redis等Key-Value数据存储方案实现高并发、大流量的Session存储。

session与cookie的区别:

1,session 在服务器端,cookie 在客户端(浏览器)

2,session 存在在服务器的一个文件里(默认),不是内存

3,session 的运行依赖 session id,而 session id 是存在 cookie 中的,也就是说,如果 浏览器禁用了 cookie
,同时 session 也会失效(当然也可以在 url 中传递)

4,session 可以放在 文件,数据库,或内存中都可以。

5,用户验证这种场合一般会用 session

因此,维持一个会话的核心就是客户端的唯一标识,即 session id

更为详尽的说法:

  1. 由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session.典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。集群的时候也要考虑Session的转移,在大型的网站,一般会有专门的Session服务器集群,用来保存用户会话,这个时候 Session 信息都是放在内存的,使用一些缓存服务比如Memcached之类的来放 Session。
  2. 思考一下服务端如何识别特定的客户?这个时候Cookie就登场了。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。实际上大多数的应用都是用 Cookie 来实现Session跟踪的,第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,我就知道你是谁了。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。
  3. Cookie其实还可以用在一些方便用户的场景下,设想你某次登陆过一个网站,下次登录的时候不想再次输入账号了,怎么办?这个信息可以写到Cookie里面,访问网站的时候,网站页面的脚本可以读取这个信息,就自动帮你把用户名给填了,能够方便一下用户。这也是Cookie名称的由来,给用户的一点甜头。

所以,总结一下:

Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;

Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。

Cookie与Session问答

  1. Cookie运行在客户端,Session运行在服务端,对吗?
  2. A:不完全正确。Cookie是运行在客户端,有客户端进行管理;Session虽然是运行在服务器端,但是sessionID作为一个Cookie是存储在客户端的。
  3. 浏览器禁止Cookie,Cookie就不能用了,但Session不会受浏览器影响,对吗?
  4. A:错。浏览器禁止Cookie,Cookie确实不能用了,Session会受浏览器端的影响。很简单的实验,在登录一个网站后,清空浏览器的Cookie和隐私数据,单机后台的连接,就会因为丢失Cookie而退出。当然,有办法通过URL传递Session。
  5. 浏览器关闭后,Cookie和Session都消失了,对吗?
  6. A:错。存储在内存中额Cookie确实会随着浏览器的关闭而消失,但存储在硬盘上的不会。更顽固的是Flash Cookie,不过现在很多系统优化软件和新版浏览器都已经支持删除Flash Cookie。百度采用了这样的技术记忆用户:Session在浏览器关闭后也不会消失,除非正常退出,代码中使用了显示的unset删除Session。否则Session可能被回收,也有可能永远残留在系统中。
  7. Session 比 Cookie 更安全吗? 不应该大量使用Cookie吗?
  8. A:错误。Cookie确实可能存在一些不安全因素,但和JavaScript一样,即使突破前端验证,还有后端保障安全。一切都还要看设计,尤其是涉及提权的时候,特别需要注意。通常情况下,Cookie和Session是绑定的,获得Cookie就相当于获得了Session,客户端把劫持的Cookie原封不动地传给服务器,服务器收到后,原封不动地验证Session,若Session存在,就实现了Cookie和Session的绑定过程。因此,不存在Session比Cookie更安全这种说法。如果说不安全,也是由于代码不安全,错误地把用作身份验证的Cookie作为权限验证来使用。
  9. Session是创建在服务器上的,应该少用Session而多用Cookie,对吗?
  10. A:错。Cookie可以提高用户体验,但会加大网络之间的数据传输量,应尽量在Cookie中仅保存必要的数据。
  11. 如果把别人机器上的Cookie文件复制到我的电脑上(假设使用相同的浏览器),是不是能够登录别人的帐号呢?如何防范?
  12. A:是的。这属于Cookie劫持的一种做法。要避免这种情况,需要在Cookie中针对IP、UA等加上特殊的校验信息,然后和服务器端进行比对。
  13. 在IE浏览器下登录某网站,换成Firefox浏览器是否仍然是未登录状态?使用IE登录了腾讯网站后,为什么使用Firefox能保持登录状态?
  14. A:不同浏览器使用不同的Cookie管理机制,无法实现公用Cookie。如果使用IE登录腾讯网站,使用Firefox也能登录,这是由于在安装腾讯QQ软件时,你的电脑上同时安装了针对这两个浏览器的插件,可以识别本地已登录QQ号码进而自动登录。本质上,不属于共用Cookie的范畴。
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×