cookie是什么?是饼干,小甜点?
No! No! No! 我今天要总结的cookie并不是你所想的小甜心,我这里要说的cookie是Web开发中的一个重要的“武器”,每一个Web开发者的武器库中肯定要装备这门武器。
cookie是浏览器存储在用户电脑上的一个文本文件,里面包含一些key=value
格式的数据;浏览器按照一定的规范来管理和存储这些数据,并在之后的请求中将这些信息发送至服务器,服务器根据客户端传回的cookie数据进行用户识别、用户行为分析等操作。千言万语总结成一句话,cookie就是:
cookie是浏览器存储在用户电脑上的一些数据;每次发起请求时,浏览器都会将对应的cookie数据一起发送至服务器。
为什么要cookie?
HTTP协议是无状态的,也就是说客户端和服务器端不需要建立持久的连接。由于客户端和服务器的连接是基于一种请求应答模式,及客户端和服务器建立一个连接,客户端提交一个请求,服务器端收到请求后返回一个响应,然后二者就断开连接。
既然客户端和服务器在完成一次请求以后,彼此就断开了连接,二者之间就不再有任何关系了;比如,用户在页面一进行了登录,当用户跳转到了同一个Web应用的页面二,那么如何在页面二知道用户已经进行了登录呢?It’s a question!!! 当客户端再次发起请求的时候,服务器端如何判断两次不同的请求来自同一个客户端呢?
是的,服务器无法区分每一次请求之间的联系。这就需要有一个状态来标识每一次请求,如果两次请求的状态标识是一样的,这就表明这两个请求是从同一个客户端发起的。
现在问题的焦点就是这个用来标识每一次请求的状态位如何来设计呢?这的确是一个难题,于是上帝派出了一位青年才俊的工程师来帮程序猿解决了这个问题,他就是24岁的网景公司的moutulli,他设计出了cookie的雏形。
在moutulli设计的基础上,经过多年的发展,cookie的发展的越来越规范,后来直接成为了标准。至于客户端浏览器和服务器之间到底怎么使用cookie,请继续阅读以下内容。
cookie的工作原理
下面我通过浏览器向www.baidu.com发起请求,并简单的将cookie的工作原理图绘制出来。
- 当我首次向百度发起首次请求时,请求头如下:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Encoding:gzip, deflate, sdch Accept-Language:en,zh-CN;q=0.8,zh;q=0.6 Connection:keep-alive Host:www.baidu.com
- 当请求到达百度的服务器以后,百度的服务器需要生成响应,并会在响应的头部写入cookie信息:
Set-Cookie:BD_HOME=1; path=/ Set-Cookie:__bsi=14934756243064632384_00_0_I_R_174_0303_C02F_N_I_I_0; expires=Thu, 19-Nov-15 14:14:50 GMT; domain=www.baidu.com; path=/ Set-Cookie:BDSVRTM=172; path=/
服务器通过发送一个带有
Set-Cookie
的HTTP消息响应头来创建一个cookie,并设置cookie的一些属性。 - 当客户端浏览器接收到响应头以后,会将cookie信息写入本地进行管理
- 当再次向百度服务器发起请求时,客户端会将之前写入的cookie一起发送过去,请求的头部信息为:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Encoding:gzip, deflate, sdch Accept-Language:en,zh-CN;q=0.8,zh;q=0.6 Connection:keep-alive Cookie:BD_HOME=1; BDSVRTM=0; BD_LAST_QID=1507196234531915875957057 Host:www.baidu.com
客户端通过发送一个带有
Cookie: name=value;
的HTTP请求头来向服务器发送本地的cookie数据。
name2=value2 - 服务器接收到请求以后,从请求头中获得cookie信息,分析cookie数据,再向客户端响应。
以上就是cookie在客户端和服务器之间进行传递信息的基本过程。
cookie的生命周期
cookie是有生命周期的,一旦到了cookie的失效日期,客户端的cookie就会被删除。服务器在创建cookie时可以控制一个cookie可以在客户端“存活”多长时间。在以下几种情况下,cookie都会结束它自己的生命周期:
- 未指定过期时间的cookie;当服务器创建一个cookie的时候没有指定对应的过期时间时,客户端会将这类cookie写入浏览器开辟的一块内存中,当关闭浏览器以后,这块内存也就被释放了,对应的cookie也就是结束了它的生命;
- 指定过期时间的cookie;当服务器创建一个cookie的时候指定了对应的过期时间时,当到达了过期时间时,对应的cookie就会被删除;
- 当浏览器中的cookie数量达到了限制时,那么浏览器就会按照某种策略删除一些旧的cookie,腾出空间来创建新的cookie;
- 当然了,我们也可以手动的人为删除cookie。
浏览器不会让cookie肆意的发展,它总会在需要的时刻出马,干掉一些cookie,结束它们的生命。
cookie的管理
我每天要浏览那么多的网站,这个网站写两个cookie,那个网站也写两个cookie,那么浏览器中的cookie肯定会很多,那么浏览器如何管理这些cookie呢?这的确是一个问题。
首先,各大浏览器都对cookie的总个数和总大小都有限制(数据来源网络,未做验证):
主流浏览器 | IE6.0 | IE7.0/8.0 | Opera | Firefox | Safari | Chrome |
---|---|---|---|---|---|---|
cookie个数 | 每个域为20个 | 每个域为50个 | 每个域为30个 | 每个域为50个 | 没有个数限制 | 每个域为53个 |
cookie大小 | 4095个字节 | 4095个字节 | 4096个字节 | 4097个字节 | 4097个字节 | 4097个字节 |
总之,在进行cookie操作的时候,应该尽量保证cookie个数小于20个,总大小小于4KB。
除了个数和大小的管理以外,每个站点各自的写的cookie,浏览器又如何管理呢?我们在服务器端创建一个cookie的时候,一般都会指定以下两个选项:
- domain选项
- path选项
这两个选项决定了创建的cookie属于哪个域名下的哪个位置。对于domain选项,默认情况下,domain会被设置为创建该cookie的页面所在的域名,所以当给相同域名发送请求时,该cookie会一起被发送至服务器。比如百度这样的大站,有很多的二级域名,例如:http://music.baidu.com/、http://picture.baidu.com/等,如果需要在所有的二级域名下都记录一个cookie,则需要在创建cookie的时候,将cookie的domain选项设置为baidu.com;此时域名为baidu.com下的所有二级域名都将拥有同样的一个cookie。创建域名时指定domain,这又是一个难点,经常会出现顶级域名和二级域名的cookie冲突问题。
我们在发送请求时,浏览器会把domain的值与请求的域名做一个尾部比较(即从字符串的尾部开始比较),并将匹配的cookie发送至服务器。所以以后在设计哪些数据需要写入cookie的时候,也要考虑清楚这个域名的问题。对于此,基本上总结为以下三点:
- 当我们未指定domain时,默认的domain为用哪个域名访问就是哪个。如果是顶级域名访问,那么设置的cookie也可以被其他二级域名所共享,因此登录等操作一般都在顶级域名下进行操作。
- 二级域名可以读取设置了domain为顶级域名或者自身的cookie,但是不能读取其他二级域名domain的cookie,因此想要cookie在多个二级域名中共享的时候,需要设置domain为顶级域名,这样就可以在所有二级域名里面使用该cookie,这里需要注意的是顶级域名只能获取到domain设置为顶级域名的cookie,无法获取domain设置为二级域名的cookie。
- 顶级域名的cookie在顶级域名或者二级域名都可以删除,但是非顶级域名访问的网站要删除顶级域名的cookie的时候,必须要设置获取到的cookie的domain为顶级域名,这样才能删除掉顶级域名的cookie,否则会无法删除。这里默认是删除访问的域名下对应的cookie,而不是顶级域名的那个。
举个例子来说:
- 我通过浏览器访问http://www.jellythink.com,请求到达服务器以后,我创建cookie时,指定的domain只能是www.jellythink.com、.jellythink.com或者jellythink.com
- 而当我通过浏览器访问http://picture.jellythink.com,请求到达服务器以后,我创建cookie时,指定的domain只能是picture.jellythink.com、.jellythink.com或者jellythink.com
说完domain,再来说说path选项。 在创建cookie的时候,也可以指定一个path值,path选项指定了请求的资源URL中只有在存在指定的路径时,才会发送Cookie消息头,它决定了客户端发送cookie到服务器端的匹配规则。通常是将path选项的值与请求的URL从头开始逐字符比较,如果字符匹配,则发送Cookie消息头。需要注意的是,只有在domain选项满足之后才会对path属性进行比较。path属性的默认值是发送Set-Cookie消息头所对应的URL中的path部分。
以上从浏览器本身的限制和生成cookie时的选项对cookie的管理进行了简单的总结。接下来就通过一些简单的代码来演示如何创建和获取cookie。
创建cookie
服务器通过发送一个带有Set-Cookie
的HTTP消息响应头来创建一个cookie。例如:
// 创建一个cookie对象
Cookie co = new Cookie("site", "http://www.jellythink.com");
co.setDomain("test.com");
// 通过响应头,将cookie发送到客户端
response.addCookie(co);
创建cookie本身没有多少难点,但是在创建创建cookie的时候,我们需要明白几个选项,以及这些选项的具体作用。
名称 | 作用 |
---|---|
domain选项 | 请参见【cookie的管理】这一小节 |
path选项 | 请参见【cookie的管理】这一小节 |
maxage选项 | 设置浏览器何时删除cookie |
secure选项 | Secure字段告诉浏览器在https通道时,对Cookie进行安全加密,这样即时有黑客监听也无法获取cookie内容;默认情况下,在HTTPS连接上传输的cookie都会被自动添加上secure选项。当secure值为true时,cookie在HTTP中是无效,在HTTPS中才有效 |
httponly选项 | HttpOnly字段告诉浏览器,只有在HTTP协议下使用,对浏览器的脚本不可见,所以跨站脚本攻击时也不会被窃取,此时JS则无法访问带有httponly的cookie |
示例代码:
Cookie co = new Cookie("site", "http://www.jellythink.com");
co.setDomain("test.com");
co.setPath("/pages");
co.setMaxAge(3600); // 单位为秒
co.setHttpOnly(true);
co.setSecure(false);
response.addCookie(co);
读取cookie
客户端向服务器发起请求时,在domain和path匹配的情况下,会将对应的cookie一起发送到服务器端。所以,如果一个path下设置的cookie太多,就可能出现http请求头超长的问题。
请求到达服务器端以后,我们可以这样读取cookies。
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; ++i) {
// 获得具体的Cookie
Cookie cookie = cookies[i];
// 获得Cookie的名称
String name = cookie.getName();
String value = cookie.getValue();
out.print("Cookie名:" + name + " Cookie值:" + value + "<br>");
}
}
总结
cookie作为每个Web开发人员必需要会的知识点,其中的细节有很多,而且经常得不到重视。这篇文章就对Web开发中的cookie这个知识点进行了详细的总结,每个容易忽视的细节,每个知识点都进行了详细的分析,虽然理论知识偏多,但是这些理论知识却是实践开发的基石,希望你能喜欢这篇文章。