HTTP状态
HTTP协议是一个无状态的协议,所有内容都位于来往的Request/Response报文之中。
但是在某些时候,我们希望能够识别用户身份,追踪用户状态/浏览路径来提供一些服务:
- 个性化的问候和欢迎
- 精准的商品推荐
- 存储用户信息,如地址和信用卡信息
- 会话状态,例如购物车
识别用户身份
HTTP首部
除了Cookie外,还有一些其他的HTTP首部可以用来发送客户端信息,可以作为了解
From:
字段携带用户Email,因为隐私和对垃圾邮件的顾虑,基本没浏览器会发送该字段User-Agent:
包含了浏览器版本,操作系统信息等内容,用来定制操作系统相关的信息时很有用Referer:
提供了跳转之前的网站URL,可以标示访问者的来源
IP地址标识
HTTP无法确定用户的IP地址,但是通过系统提供的Socket API可以从TCP报文中获取客户端的IP地址。
利用IP地址来确认用户身份具有非常大的缺点:
- IP地址标识了机器,公共计算机可能被不同的用户使用
- 许多用户通过NAT技术来接入互联网,IP地址后很可能隐藏了许多不同用户
- ISP(因特网服务提供商)的动态IP,代理网关等,都可能导致用户IP的变化
用户登陆机制
网站可以使用包括HTTP内建登陆机制在内的登陆机制来识别用户身份
登陆机制是一种比较完善,安全的状态记录机制,但是对于用户来说,依然不够友好,对于许多轻量级应用(如精准广告投放,购物车),让用户记录一串密码账号是非常糟糕并且不现实的
胖URL
胖URL是一种将用户的身份写在URL里的状态保存方法。当用户第一次连接时,在URL里加入一些特殊内容来标示身份,之后呈现给用户的所有HTML页面的URL连接都被渲染为包含了该ID的特殊URL。
通过胖URL,亚马逊这样的电商可以追踪用户本次连接访问的商品,实现购物车,精准推荐等功能
如之前的这些解决方法,胖URL也有着许多缺点:
- 无法共享的URL:共享会将自己的身份ID发出去,导致隐私泄露
- 加重服务器荷载,服务器需要实时渲染大量不同的HTML页面
- 代码难于书写维护,用户容易点击普通URL从而失去身份
- 不持久,当用户离开会话再次连接,无法确认上次的身份
Cookie
Cookie是在HTML5的LocalStorage
和SessionStorage
之前最佳的持久会话实现方式,所有主要浏览器都支持该功能,其将会话状态保存在HTTP首部中
Cookie是浏览器和服务器实现的一种首部,而不是HTTP/1.1规范里的
规范
Cookie首部的规范有版本0和版本1两个标准,后者是对前者的加强和扩充
本地Cookie的存储则根据浏览器的不同实现不同,一般作为一个表存储在文件里
结构
Cookie版本0的核心是两个分别在请求和响应中出现的首部:
Set-Cookie: name=value [; expires=date] [; path=path] [; domain=domain] [;secure]
Cookie: name=value1 [; name2 = value2]
服务器通过Set-Cookie
首部设置 Key-Value Pair,其可选项可以设置一些参数:
Expires
: 不设置过期时间意味着关闭浏览器后消失(会话生命周期)Domain
: 只向指定域发送Cookie,至少要2-3个dot,不允许.com
这类用法,不设置则默认为响应的服务器的主机名Path
: 在Domain的基础上,与该路径匹配,如/foo
与/foo/a
,/foo/b
匹配,默认为当前响应的路径Secure
: 如果设置该属性,则只有在HTTPS协议下发送该Cookie
浏览器在上述参数符合的情况下将在请求头附带Cookie
首部传递存储的Cookie
在Cookie版本1加入了一些其他首部,具体可以查询标准
会话过程
服务器通常通过这样的一个过程来完成Cookie的附加:
- 客户端发送请求到服务器
- 服务器响应302重定向客户端到Cookie附加页面
- 客户端发送请求到重定向页面
- 重定向页面附加Cookie并再次发送302重定向到正常入口
- 客户端再次发送请求到重定向页面
- 服务器响应,进入正常入口,通过Cookie追踪身份
缺点
Cookie是目前Client/Server通信最好的方式(LocalStorage
必须依靠其他方法向服务器通信), 但是依然有许多缺点:
- 大小与个数受限, 通常为4K
- 存在风险, 用户和恶意程序可能篡改Cookie
- 可能被用户禁用, 导致功能无法使用