Http权威指南笔记(九)——客户端识别和cookie机制

按照《HTTP权威指南》的章节,在这之前应该还有“Web机器人”和“HTTP-NG”两个章节,但是这两个章节实际使用当中比较少,这里就没有做介绍,感兴趣的朋友可以自己去看看。本节我们开始介绍HTTP识别、认证和安全部分的客户端识别和cookie机制。

HTTP属于无状态协议,一般情况服务器是没法得知分别发送请求的客户端用户的。但是我们大部分Web站点都需要或者希望能够区分用户,根据用户的不同兴趣爱好等提供更好的个性化服务。所以本节我们就来看下客户端识别相关知识。

1 HTTP首部

HTTP中提供了几种可以用于承载用户信息的首部,如下表所示:

首部名称 首部类型 描  述
From 请求 用户的 E-mail 地址
User-Agent 请求 用户的浏览器软件
Referer 请求 用户是从这个页面上依照链接跳转过来的
Authorization 请求 用户名和密码(稍后讨论)
Client-IP 扩展(请求) 客户端的 IP 地址(稍后讨论)
X-Forwarded-For 扩展(请求) 客户端的 IP 地址(稍后讨论)
Cookie 扩展(请求) 服务器产生的 ID 标签(稍后讨论)

这里我们先介绍下前面三种,后面四种相对比较复杂,我们会分小节单独介绍。

  • From:该首部可以用于指定用户的Email地址,但是由于暴露了Email地址,而且有些服务器或者代理商又喜欢收集用户的Email地址用作其他用途,所以该首部相对较少使用。
  • User-Agent:是用于指定用户所使用客户端(如浏览器)的相关信息,比如我们常用的Chrome浏览器的该头部信息如下:
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
  • Referer:该首部提供了用户来源页面的 URL。Referer 首部自身并不能完全标识用户,但它确实说明了用户之前访问过哪个页面。

2 客户端IP地址

一般来说HTTP的客户端是不会再请求中提供IP地址的(某些代理可能会提供Client-IP首部),但是服务器端可以通过响应途径找到TCP连接的另一端的IP地址的。
虽然服务器端能获得客户端的IP地址,但是将IP地址作为用户的标识是不可行或者说不完善的。主要原因如下:

  1. 现在的计算机大部分都是多用户系统,同一个IP地址可以对应多个用户。
  2. 而且现在由于IP地址比较紧缺,很多网络服务商会通过网络地址转换器(NetWork Address Translation,NAT)提供服务,这些NAT设备会隐藏客户端的IP,所以客户端共享一个对外的IP。
  3. 如果中间有经过代理,服务器获获取到的就是代理的IP,而不是客户端的IP地址。

3 用户登录

Web 服务器可以要求用户通过用户名和密码进行认证(登录)来显式地询问用户是谁。
为了使 Web 站点的登录更加简便,HTTP 中包含了一种内建机制,可以用 WWW-Authenticate 首部和 Authorization 首部向 Web 站点传送用户的相关信息。一旦登录,浏览器就可以不断地在每条发往这个站点的请求中发送这个登录信息了,服务器就可以通过登录信息进行客户端识别了。具体认证方式后面会有专门的章节进行介绍,这里就不详述了。

4 胖URL

有些 Web 站点会为每个用户生成特定版本的 URL 来追踪用户的身份。通常,会对真正的 URL 进行扩展,在 URL 路径开始或结束的地方添加一些状态信息。用户浏览站点时,Web 服务器会动态生成一些超链,继续维护 URL 中的状态信息。改动后包含了用户状态信息的 URL 被称为胖 URL(fat URL)。后面服务器就可以根据请求中的URL携带的=额外信息进行客户端识别了。但是这个也存在一些问题:

  • 无法共享URL:因为URL里面携带有个人的识别信息,所以不能将URL进行共享,共享就可能会泄露个人信息
  • 缓存问题:由于所有的URL都添加了个人识别信息,这个时候即使请求的同一个资源,但是缓存的时候也会为每个用户进行进行单独缓存,会非常浪费资源。
  • 增加服务器负荷:由于每个用户需要单独重新生成特定的URL,也就意味着所有的HTML等资源中的超链接都需要为每个用户进行单独修改

5 cookie

cookie是目前所有识别用户信息方式中最好的一种,所以也是最为流行的一种,也是本小节的重点。

5.1 cookie的类型

cookie大体上可以分为两类,一种是在一次会话有效的临时cookie,当用户退出浏览器结束本次会好的时候,就会失效。另一种是持久cookie,这种cookie一般会被保存在硬盘等存储介质上,只要cookie没有过期,就一直有效。即使用户退出浏览器,下次打开访问的时候,如果cookie还在有效期,就能使用。
区别两种cookie的方式,就是只要使用了Discard参数或者没有设置Expires或者Max-age参数,就属于临时cookie,其他这属于持久cookie。

5.2 cookie的作用过程

cookie的作用原理就是:Web站点会给客户端贴一个标识,后续每当客户端访问这个Web站点的时候就带上这个标识,Web站点就能通过这个标识知道是哪个客户端在访问了。过程如下图所示:
cookie原理
如上图所示,当客户端第一次访问该服务器的时候,Web服务器会的响应内容中会包含一个Set-cookie的响应头,里面包含一些cookie信息,客户端收到响应后就会记下(保存)该cookie的信息,后续每次访问该Web服务器就会携带上指定的cookie信息。至于cookie的保存方式,这个就客户端自行决定了,只要客户端自己能找到和识别就行。

5.3 不同的站点的cookie区分

一个客户端不可能只访问一个Web站点,可能会访问很多个Web站点,而每个Web站点都会自己的cookie,所以客户端保存的cookie也是有很多的。客户端在发送cookie的时候就会有筛选过程。那客户端是怎么知道在访问不同的资源的时候发送哪个cookie呢?一般就是通过cookie的domain和path属性进行控制。客户端在发送cookie的时候,会对domain和path进行判断,选取匹配的cookie属性值进行发送。这里对domain和path就不展开说明,每个属性后面小节会专门介绍cookie各个版本的属性。

5.4 cookie的属性介绍

现在使用的 cookie 规范有两个不同的版本:cookies 版本 0(有时被称为 Netscape cookies) 和 cookies 版本 1(RFC 2965)。cookies 版本 1 是对 cookies 版本 0 的扩展。所以这里对cookie属性的介绍也分为两个版本进行介绍。

5.4.1 cookies版本0的属性介绍

  1. 这里先介绍服务器使用的Set-Cookie首部
    Set-Cookie 首部有一个强制性的 cookie 名和 cookie 值。后面跟着可选的 cookie 属性,中间由分号分隔。具体每个属性介绍如下表所示:
Set-Cookie属性 描述及实例
NAME=VALUE 强制的。NAME 和 VALUE 都是字符序列,除非包含在双引号内,否则不包括分号、逗号、等号和空格。Web 服务器可以创建任意的 NAME=VALUE 关联,在后继对站点的访问中会将其送回给 Web 服务器:Set-Cookie: customer=Mary
Expires 可选的。这个属性会指定一个日期字符串,用来定义cookie 的实际生存期。一旦到了过期日期,就不再存储或发布这个 cookie 了。日期的格式为:Weekday, DD-Mon-YY HH:MM:SS GMT
唯一合法的时区为GMT,各日期元素之间的分隔符一定要是长划线。如果没有指定Expires, cookie 就会在用户会话结束时过期。如:
Set-Cookie: foo=bar; expires=Wednesday, 09-Nov-99 23:12:40 GMT
Domain 可选的。浏览器只向指定域中的服务器主机名发送cookie。这样服务器就将 cookie 限制在了特定的域中。acme.com 域就与 anvil.acme.comshipping.crate.acme.com 相匹配,但与 www.cnn.com 就不匹配了。只有指定域中的主机才能为一个域设置cookie,这些域中至少要有两个或三个句号,以防止出现.com、.edu 和 va.us 等形式的域。这里列出了一组固定的特定高层域,落在这个范围中的域只需要两个句号。所有其他域都至少需要三个句号。特定的高层域包括:.com、.edu、.net、.org、.gov、.mil、.int、.biz、.info、.name、.museum、.coop、.aero 和.pro
如果没有指定域,就默认为产生Set-Cookie 响应的服务器的主机名:Set-Cookie: SHIPPING=FEDEX; domain=“joes-hardware.com
Path 可选的。通过这个属性可以为服务器上特定的文档分配cookie。如果 Path 属性是一个 URL 路径前缀,就可以附加一个cookie。路径 /foo 与 /foobar 和 /foo/bar.html 相匹配。路径“/”与域名中所有内容都匹配。如果没有指定路径,就将其设置为产生 Set-Cookie 响应的 URL 的路径:Set-Cookie: lastorder=00183; path=/orders
Secure 可选的。如果包含了这一属性,就只有在 HTTP 使用 SSL 安全连接时才会发送 cookie:Set-Cookie: private_id=519; secure
  1. 客户端使用的Cookie首部
    客户端这一端相对比较简单,一般根据需要将合适的Cookie属性发送给服务器即可。在版本0的客户端在发送请求时,会将所有有关需要的cookie属性组合一个
    Cookie首部中,如:
    Cookie: session-id=002-1145265-8016838; session-id-time=1007884800

5.4.2 cookie版本1的属性介绍

和版本0相比,该版本的cookie引入了标准的Set-Cookie2和Cookie2两个首部。同版本0相比主要区别如下:

  • 可以为每个cookie添加解释性文章,对其进行描述
  • 允许客户端在退出时候,不考虑过期时间,将cookie强制销毁
  • 使用相对秒数(Max-Age),而不是绝对时间来设置cookie的过期时间
  • 除了domain和path外,还可以通过端口号区分cookie
  • 在Cookie首部中,如果是使用关键字,在其前面加上“$”符号的前缀表示是关键字,而不是普通的cookie值。

下面接具体介绍每个数限制的,同样的我们还是先看Set-Cookie2的属性。

  1. Set-Cookie2的属性
Set-Cookie2属性 描述及实例
NAME=VALUE 强制的。Web 服务器可以创建任意的 NAME=VALUE 关联,可以在后继对站点的访问中将其发回给 Web 服务器。“$ ”是保留字符,所以名字一定不能以它开头
Version 强制的。这个属性的值是一个整数,对应于 cookie 规范的版本。RFC 2965 为版本1:Set-Cookie2: Part=“Rocket_Launcher_0001”; Version=“1”
Comment 可选。这个属性说明了服务器准备如何使用这个cookie。用户可以通过检查此策略来确定是否允许使用带有这个 cookie 的会话。这个值必须采用 UTF-8 编码
CommentURL 可选。这个属性提供了一个 URL 指针,指向详细描述了 cookie 目的及策略的文档。用户可以通过查看此策略来判定是否允许使用带有这个 cookie会话。
Discard 可选。如果提供了这个属性,就会在客户端程序终止时,指示客户端放弃这个 cookie
Domain 可选。浏览器只向指定域中的服务器主机名发送 cookie。这样服务器就可以将 cookie 限制在特定域中了。acme.com 域与主机名 anvil.acme.comshipping.crate.acme.com 相匹配,但不匹配于www.cnn.com。域名匹配的规则基本上与 Netscape cookie 一样,但有几条附加的规则。细节请参见 RFC 2965
Max-Age 可选。这个属性的值是一个整数,用于设置以秒为单位的 cookie 生存期。客户端应该根据 HTTP/1.1 的使用期计算规则来计算 cookie 的使用期。cookie 的使用期比 Max-Age 大时,客户端就应该将这个 cookie 丢弃。值为零说明应该立即将那个 cookie 丢弃
Path 可选。通过这个属性可以为服务器上的特定文档指定 cookie。如果 Path 属性是一个 URL 路径的前缀,就可以附加一个 cookie。路径 /foo 匹配于 /foobar 和 /foo/bar.html。路径“/”匹配于域中所有内容。如果没有指定路径,就将其设置为生成 Set-Cookie 响应的URL 的路径
Port 可选。这个属性可以单独作为关键字使用,也可以包含一个由逗号分隔的、可以应用 cookie 的端口列表。如果有端口列表,就只能向端口与列表中的端口相匹配的服务器提供 cookie。如果单独提供关键字 Port 而没有值,就只能向当前响应服务器的端口号提供 cookie:
Set-Cookie2: foo=“bar”; Version=“1”;
Port="80,81,8080"
Set-Cookie2: foo=“bar”; Version=“1”; Port
Secure 可选。如果包含这个属性,就只有在 HTTP 使用 SSL 安全连接时才能发送 cookie
  1. 版本1的Cookie首部
    对于Cookie首部来说,版本1和版本0基本没有差别,也是一样根据相关属性筛选出来Cookie后,一般组合在一起进行发送。

5.5 关于版本0和版本1的使用

一般来说,对于能够支持Cookie2的客户端,要同时做好兼容Cookie的准备,保证服务器返回的是Set-Cookie首部,也能正确处理。如果客户端同时受到了Set-Cookie2和Set-Cookie,那么应该选用Set-Cookie2,忽略Set-Cookie首部。
对于服务端来说,如果客户端明确告知了其支持版本1,那服务器在返回响应的时候,也要优先选择返回Set-Cookie2的首部。

5.6 Cookie作用与会话跟踪的过程

Cookie可以作为客户端识别和会话跟踪技术。那他们具体实现过程如下图所示:
Cookie工作工程
从上图中可以看到,一开始客户端发起的是一个不带有Cookie首部的普通请求,服务器再收到该请求后,会返回重定向响应,同时携带Set-Cookie首部。客户端再收到该响应后,会携带上Cookie首部,然后重新对服务器指定的URL发起访问,最终直到服务器返回所请求的正确资源。

5.7 cookie与缓存

如果我们的请求过程中有cookie的参与,那么我们在处理缓存的时候就需要特别消息。因为很多cookie可能都和用户的隐私相关,如果我们不加处理的缓存了所有数据,很有可能造成数据混乱设置用户的隐私数据的暴露。一般我们在对有cookie的数据进行缓存的时候,采取如下几个原则进行处理:

  • 服务器如果明确知道该文档资源是不能被缓存的,那么它在响应的时候,应该明确标识出来(Cache-Control:no-cache="Set-Cookie"),告诉代理不能缓存该文档。如果能缓存,也可以明确告诉客户端最好(Cache-Contrl:public
  • 如果服务器没有明确说明该资源不能缓存,此时缓存的时候,要特别小心Set-Cookie的处理,如果此时我们仅仅不对Set-Cookie首部进行缓存,但是内容我们缓存了,这种情况下次客户端在发起请求的时候,如果使用了缓存资源,这个时候客户端收到的响应就没有Set-Cookie首部,所以也会引起一些问题。当然,最好的控制还是在原始服务器,原始服务器可以要求每次使用缓存的时候都进行验证(Cache-Control: must-revalidate, max-age=0),这样就能获取到Set-Cookie首部了。如果服务器什么都没有提供,那么保守的做法就是不要缓存带有Set-Cookie的文档资源,一些图片资源却可以缓存。
  • 最后一条就是对服务器要求,也就是我们上面第一条和第二条中说到,对于有Set-Cookie首部的内容,最好的缓存控制是在服务器端,所以我们的服务器要尽量做到对缓存控制的精细,要告知代理缓存具体应该怎么去处理。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章