本文作者mcrwayfun,转载自简书,转载时有改动

HTTP 协议本身是无状态的。什么是无状态呢,即服务器无法判断用户身份,无状态是指 Web 浏览器与 Web 服务器之间不需要建立持久的连接,这意味着当一个客户端向服务器端发出请求,然后 Web 服务器返回响应(Response),连接就被关闭了,在服务器端不保留连接的有关信息,也就是说,HTTP 请求只能由客户端发起,而服务器不能主动向客户端发送数据。

Cookie 实际上是一小段的文本信息(key-value 格式)。客户端向服务器发起请求,如果服务器需要记录该用户状态,就使用 response 向客户端浏览器颁发一个 Cookie。客户端浏览器会把 Cookie 保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该 Cookie 一同提交给服务器。服务器检查该 Cookie,以此来辨认用户状态

打个比方,我们去银行办理储蓄业务,第一次给你办了张银行卡,里面存放了身份证、密码、手机等个人信息。当你下次再来这个银行时,银行机器能识别你的卡,从而能够直接办理业务

当用户第一次访问并登陆一个网站的时候,Cookie 的设置以及发送会经历以下 4 个步骤:

客户端发送一个请求到服务器,
服务器发送一个 HttpResponse 响应到客户端,其中包含 Set-Cookie 的头部,
客户端保存 Cookie ,之后向服务器发送请求时,HttpRequest 请求中会包含一个 Cookie 的头部,
服务器返回响应数据

为了探究这个过程,写了代码进行测试,如下:

我在 doGet 方法中,new 了一个 Cookie 对象并将其加入到了 HttpResponse 对象中

1
2
3
4
5
6
7
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

Cookie cookie = new Cookie("CHINQ",System.currentTimeMillis()+"");
// 设置生命周期为MAX_VALUE
cookie.setMaxAge(Integer.MAX_VALUE);
resp.addCookie(cookie);
}

Response Headers 中包含 Set-Cookie 头部,而 Request Headers 中包含了 Cookie 头部。name 和 value 正是上述设置的

属性项 属性项介绍
Name = Value 键值对,可以设置要保存的 Key / Value,注意这里的 Name 不能和其他属性项的名字一样
Expires 过期时间,在设置的某个时间点后该 Cookie 就会失效
Domain 生成该 Cookie 的域名,如 domain=”www.chinq.xyz"
Path 该 Cookie 是在当前的哪个路径下生成的,如 path=/post/
Secure 如果设置了这个属性,那么只会在 SSL 连接时才会回传该 Cookie

Expires

该属性用来设置 Cookie 的有效期。Cookie 中的 maxAge 用来表示该属性,单位为秒。Cookie 中通过 getMaxAge () 和 setMaxAge (int maxAge) 来读写该属性。maxAge 有 3 种值,分别为正数,负数和 0

如果 maxAge 属性为正数,则表示该 Cookie 会在 maxAge 秒之后自动失效。浏览器会将 maxAge 为正数的 Cookie 持久化,即写到对应的 Cookie 文件中(每个浏览器存储的位置不一致)。无论客户关闭了浏览器还是电脑,只要还在 maxAge 秒之前,登录网站时该 Cookie 仍然有效。下面代码中的 Cookie 信息将永远有效

1
2
3
4
Cookie cookie = new Cookie("CHINQ",System.currentTimeMillis()+"");
// 设置生命周期为MAX_VALUE,永久有效
cookie.setMaxAge(Integer.MAX_VALUE);
resp.addCookie(cookie);

当 maxAge 属性为负数,则表示该 Cookie 只是一个临时 Cookie,不会被持久化,仅在本浏览器窗口或者本窗口打开的子窗口中有效,关闭浏览器后该 Cookie 立即失效

1
2
3
4
Cookie cookie = new Cookie("CHINQ",System.currentTimeMillis()+"");
// MaxAge为负数,是一个临时Cookie,不会持久化
cookie.setMaxAge(-1);
resp.addCookie(cookie);

当 maxAge 为 0 时,表示立即删除 Cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Cookie[] cookies = req.getCookies();
Cookie cookie = null;

// Get Cookie
for (Cookie ck : cookies) {

if ("CHINQ".equals(ck.getName())) {
cookie = ck;
break;
}
}

if (null != cookie) {
// Delete the cookie
cookie.setMaxAge(0);
resp.addCookie(cookie);
}

那么 maxAge 设置为负值和 0 到底有什么区别呢?

maxAge 设置为 0 表示立即删除该 Cookie,如果在 debug 的模式下,执行上述方法,可以看见 cookie 立即被删除了

maxAge 设置为负数,能看到 Expires 属性改变了,但 Cookie 仍然会存在一段时间直到关闭浏览器或者重新打开浏览器

HttpServletResponse 提供的 Cookie 操作只有一个 addCookie (Cookie cookie),所以想要修改 Cookie 只能使用一个同名的 Cookie 来覆盖原先的 Cookie。如果要删除某个 Cookie,则只需要新建一个同名的 Cookie,并将 maxAge 设置为 0,并覆盖原来的 Cookie 即可

新建的 Cookie,除了 value、maxAge 之外的属性,比如 name、path、domain 都必须与原来的一致才能达到修改或者删除的效果。否则,浏览器将视为两个不同的 Cookie 不予覆盖

值得注意的是,从客户端读取 Cookie 时,包括 maxAge 在内的其他属性都是不可读的,也不会被提交。浏览器提交 Cookie 时只会提交 name 和 value 属性,maxAge 属性只被浏览器用来判断 Cookie 是否过期,而不能用服务端来判断

我们无法在服务端通过 cookie.getMaxAge () 来判断该 cookie 是否过期,maxAge 只是一个只读属性,值永远为 - 1。当 cookie 过期时,浏览器在与后台交互时会自动筛选过期 cookie,过期了的 cookie 就不会被携带了

Cookie 是不可以跨域名的,隐私安全机制禁止网站非法获取其他网站的 Cookie

正常情况下,同一个一级域名下的两个二级域名也不能交互使用 Cookie,比如 Cookie-1.chinq.xyz 和 Cookie-2.chinq.xyz,因为二者的域名不完全相同。如果想要 chinq.xyz 名下的二级域名都可以使用该 Cookie,需要设置 Cookie 的 domain 参数为.chinq.xyz,这样使用 Cookie-1.chinq.xyz 和 Cookie-2.chinq.xyz 就能访问同一个 Cookie

path 属性决定允许访问 Cookie 的路径。比如,设置为 “/“ 表示允许所有路径都可以使用 Cookie