一、个性化
HTTP
最初是一个匿名,无状态的 请求/响应 协议,Web
服务器几乎没有什么信息可以用来判定是哪一个用户发来的请求,后来开发者们总结了一些识别机制来标识用户:
- 包含用户信息的
HTTP
首部 - 通过客户的
IP
地址来识别 - 用认证的方式识别
- 胖
URL
,把用户信息嵌入在URL
中
二、通过首部识别
以下是常见用来承载用户相关的HTTP
请求首部:
-
from
,用户的邮箱地址 -
user-agent
,用户的浏览器软件 -
referer
,请求来自的页面 -
authorization
,用户名和密码 -
client-ip
,客户端ip
地址(扩展) -
x-forwarded-for
,客户端ip
地址(扩展) -
cookie
,用户标识
一般来说,没有浏览器或者用户会使用from
,因为担心邮箱泄露导致大量的垃圾邮件,这个头部一般用于正规爬虫。user-agent
可以告诉服务端请求来自何种服务器,实现与浏览器相关的定制内容返回。referer
这个首部也不可靠,因为可以手动伪造(有的浏览器会禁止修改这个首部)。
三、客户端IP地址
web
浏览器可以找到承载HTTP
请求的tcp
连接对应的目的ip
,但是用ip
来标识客户并不可靠,主要有以下原因:
-
ip
描述的是机器而非用户 -
ip
一般由服务商动态分配 -
ip
地址是有限的,所以很多用户是通过NAT
接入网络的 - 有时只能获取到代理的
ip
而非客户端的
四、用户登录
首先服务端返回错误码401 login required
,此时浏览器弹出登录对话框,用户输入并确认后浏览器会把用户的信息放在authorization
首部中发送给服务端,并且会保存下来,之后每次请求都会带上该首部。这样做的好处是用户只需要登录一次,并且相对可靠。缺点是不安全,把用户名密码直接放在首部很容易被其他观察者直接看到并泄露。
五、胖URL
有的网站会为每个用户生成特殊的识别码并嵌入url
中,并以此来标识用户,而且还可以在url
中追加一些用户状态等等,这样做对用户来说很方便,几乎无感知,因为主要通过服务端重写url
来完成,但是缺点也很多:
- 过长的
url
- 无法共享连接,因为你的连接中包含你的个人信息
- 无法缓存,因为缓存是和
url
相关的,如果每个人的url
都不相同,那么缓存就失效了 - 加重服务器负载
- 不稳定,如果用户在该页面已经操作了很多内容,一旦用户丢失了该连接,那么将永远无法找到
六、cookie
cookie
是目前实现持久会话的最好的方式,现在所有主流浏览器都支持。
6.1 cookie的类型
我们可以吧cookie
大致分为2类:会话cookie
和持久cookie
。会话cookie
在关闭页面后就会失效,而持久cookie
即使关闭页面也能保存一定时间。
6.2 客户端侧状态
cookie
的基本思想是让浏览器积累一组服务器特有的信息,每次访问服务器时提供这些信息,因为信息是存储在客户端的,所以此系统被称为客户端侧状态。
6.3 不同的站点使用不同的cookie
浏览器内部存有很多的cookie
,但是不会在访问网站的时候带上所有的cookie
,因为cookie
只是一些键值对,只对设置这些cookie`的网站有效,对其他人来说都是不可读的垃圾数据。
6.3.1 cookie的域属性
set-cookie
可以给响应首部添加一个domain
属性来控制那些站点可以使用该cookie
。
set-cookie: user="sadhgiaq3124jaklh"; domain="xxx.com"
6.3.2 cookie的path属性
除了设置域,还可以设置path
,这样可以减少无用cookie
传播,只有访问特定的path
才会携带该cookie
。
set-cookie: pref="color"; domain="xxx.com"; path="/pages/"
如果设置了以上2个cookie
,访问www.xxx.com
时携带:
cookie:user="sadhgiaq3124jaklh";
访问www.xxx.com/pages/index.html
时携带:
cookie:user="sadhgiaq3124jaklh";pref="color"
6.4 cookie的组成
目前使用的cookie
有2个版本:版本0和版本1,后者是前者的扩展,但应用不及前者广泛
6.4.1 版本0(Netscape)
set-cookie
首部:
-
name=value
,必须的 -
expires=date
,可选的,指定cookie
过期时间 -
domain=value
,可选的,设置cookie
指定域,如果没有设置,那么默认为当前主机名 -
path=value
,可选的,指定路径,如果没设置,那么默认当前路径 -
secure
,可选的,如果包含这个属性,那么只有在https
连接中才会携带该cookie
使用set-cookie
设置的cookie
在请求时会被合并到一个cookie
首部中。
6.4.2 版本1(RFC2965)
RFC2109
定义了一个cookie
的扩展版本,引入了set-cookie2
和cookie2
首部,下面是set-cookie2
的可用首部:
-
name=value
,必须的,同版本0 -
version
,必须的,一个整数,set-cookie2:version= "1"
-
comment
,可选的,说明服务器如何使用该cookie
-
comment-url
,可选的,提供一个url
指针,指向的文档详细说明cookie
使用文档 -
discard
,可选的,表示当前cookie
为会话cookie
,会话结束后直接销毁 -
domain
,同版本0 -
max-age
,可选的,相对过期时间 -
path
,可选的,同版本0 -
port
,可选的,只能向指定端口提供cookie
-
secure
,同版本0
该规范的cookie2
首部里所有关键字都以$开头。
6.4.3 版本协商
因为版本1的cookie
扩展功能更多,所以当请求链路上的设备都支持版本1的话,那应该使用版本1,下面描述各设备之间如何协商cookie
版本:
- 服务器响应请求时会发送
set-cookie
和set-cookie2
,客户端如果支持版本1cookie
,那么会丢弃set-cookie
的值 - 如果服务端未使用
set-cookie2
,客户端可以发送cookie2: $version=1
告知服务端可以使用set-cookie2
6.5 cookie与缓存
当公共缓存缓存带有set-cookie
的响应时应该格外小心,下面是一些处理缓存需要注意的规则:
- 如果该文档可以缓存,但是不希望缓存
set-cookie
首部,服务器应道注明cache-control:no-cache="set-cookie"
。 - 当服务端通过某个请求去设置
cookie
的时候,需要让这个请求使用协商缓存,即每一个请求必须到达服务器,否则当代理缓存中有该请求的缓存,那么其他客户端就无法获得cookie
。有些比较严格的代理会舍弃一切带有set-cookie
首部的副本。 - 当服务端接收到带有
cookie
的请求时一定要注意,这个响应可能是私有的,那么服务端应该设置好响应首部避免该响应被公共缓存保存。
参考资料
[1]《HTTP权威指南》