“会话技术”——Cookie_(2/2)原理与使用细节
经过Cookie的快速入门与代码使用。如果想深入理解Cookie的技术实现,就得去理解它的原理。
且有些时候使用Cookie,还要根据需求设置存活期限以及确定Cookie获取范围等其他细节。最后,我们会总结Cookie这门客户端会话技术的作用。
一、原理
注:我们还是使用简易图画进行辅助理解
注:这是上一节写的代码(两个Servlet),这里将这个例子使用一下分析Cookie的原理
所谓分析Cookie原理,正是分析: 服务器端如何响应时发送Cookie,以至于浏览器端识别了还自动保存;并且CookieDemo2怎么获取到的Cookie,即浏览器怎么就携带了Cookie信息进行了下一次请求。
1.1 分析:
其实不管发送数据也好,还是获取数据(站在服务器端看)也好,它们在客户端与我端(服务端)中间都是以HTTP协议进行交互——响应和请求一定是遵循HTTP协议的。
首先,客户端发送了第一次请求,请求访问了服务器端的资源——CookieDemo1。
CookieDemo1收到了请求,于是以“发送Cookie”进行处理数据并响应了(确实向浏览器发送了Cookie),刚刚说“所有的客户与服务器端的响应请求都遵循HTTP协议”。所以那实际上,“发送Cookie”时,怎么发送呢? 服务器(response端)会在响应报文中设置一个名为“Set-Cookie”的响应头,该响应头的值以键值对的形式呈现,恰好可用于存储Cookie
于是浏览器端会收到来自服务器端的响应报文,发现响应报文的响应头有“set-Cookie”。浏览器因为熟读HTTP协议,所以遵循协议:如果浏览器收到了set-Cookie响应头,它应该将这个头里携带的数据(“name=Bear”)保存到自己本地中。
而在第二次请求时,HTTP协议又规定了:浏览器应该在下一次请求中将保存的数据(“name=Bear”)放进一个Cookie的请求头中(作为值)。浏览器端会在请求报文中设置一个Cookie请求头,其值类型也为键值对,恰好也用来放Cookie数据。
于是CookieDemo2能从请求头中获取到Cookie信息。但是,在咱们使用Cookie代码获取请求里的Cookie,并不是操作所谓的“Cookie这个请求头”,而是怎么?
Cookie[] cookies = request.getCookies();
//这样一个名叫getCookies()的方法
这也得多亏了JAVA EE 内部实现的API,封装的库方法(函数)。让我们调用库里的方法就能发送和获取Cookie,而方法便是去和请求头或者响应头打交道,我们放心调用就行。
1.2 看看:
初见"set-Cookie"响应头和“Cookie”请求头,不熟悉很正常,多看几眼。我们在浏览器抓包
1.2.1抓包
(浏览器端)按F12键 ,接着如图操作:
我们现在运行,访问CookieDemo1—— :抓抓它的响应包:
是吧,这发送Cookie响应时会真的有一个响应标头Set-Cookie。
那如果我们继续请求CookieDemo2:抓抓它的请求报文,是不是在存储Cookie后下一次请求就会在请求头里捎带上Cookie数据。
确实是。
二、Cookie的细节
2.1 一次可不可以发送多个cookie呢?
了解了原理这个问题== 响应报文set-Cookie的值可以是多个键值对吗?
我们修改修改CookieDemo1:
// 原
@WebServlet("/CookieServlet01")
public class CookieServlet01 extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request,response);}protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{// 1. 创建Cookie,绑定数据Cookie cookie = new Cookie("name","Bear");// 2. 发送Cookie对象response.addCookie(cookie);}}
我们再创建一个Cookie,让response调用两次addCookie();
protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{// 1. 创建Cookie,绑定数据Cookie cookie = new Cookie("name","Bear");Cookie cookie2 = new Cookie("place","Toilet");//新建// 2. 发送Cookie对象response.addCookie(cookie);response.addCookie(cookie2);//连着发送}
运行这段代码,即重新请求CookieDemo1,抓抓它的请求包:
答案:一次可以发送多个Cookie。
为了完整,我们再访问打印Cookie的那个Servlet,抓抓这回的请求包:
都存了,是的,刚刚响应发来的Cookie都存进浏览器了;这会儿,也全部放进请求头Cookie了。
2.2 Cookie在浏览器中能保存多长时间?
默认情况下,当浏览器关闭后,Cookie数据被销毁;因为默认存到浏览器内存里,程序结束,内存就会删除数据。
如果想设置持久化存储,我们这里再学习一个Cookie的方法:
cookie.setMaxAge(int seconds)
//参数是int类型,单位为秒数。
// int 是有符号整型,于是根据取值的不同,该方法会实现不同的效果
若seconds 为 正数 :意味着 a. 将Cookie数据写到硬盘的文件中、持久化存储。
b. 规定了该Cookie存活的时间
若seconds 为 0 : 意味着删除该Cookie。设置完cookie的MaxAge,需要将这个Cookie发送给客户端。
为什么秒数为0,就意味着删除? 这句话看似只有1个问,其实有2个答案。
1. Cookie 最大存活期限为0,那么就意味着“不存在”。
2. Cookie是存在用户端的数据,服务器端不能直接操作客户端的数据,所以服务器想要删除Cookie就用这种方式:设MaxAge=0,再响应发送。
若seconds为 负数:意味着默认设置(所以设为负数,跟不设置MaxAge效果一样:一旦关闭浏览器,存在内存里的Cookie就会被释放,失效)
代码实践timing :
我们新建一个CookieServlet03来创建和发送Cookie。
/** CookieDemo03 用来创建和发送Cookie* 分别设置存活期限:* ^ 正数* ^ 负数* ^ 0* */
@WebServlet("/CookieServlet03")
public class CookieServlet03 extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request,response);}protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{// 1. 创建Cookie,绑定数据// 2. 设置Cookie的存活期限// 3. 发送Cookie对象}
}
写入核心代码:
// 1. 创建Cookie,绑定数据Cookie cookie = new Cookie("name","bear");// 2. 设置Cookie的存活期限cookie.setMaxAge(30);//设置正数的存活期限,代表了:1.将Cookie数据写到硬盘的文件中。持久化存储。2.Cookie存活时间// 3. 发送Cookie对象response.addCookie(cookie);
验证逻辑:我们重新打开浏览器,先访问CookieServlet03(客户端收到cookie会存起来),再请求访问CookieDemo02(这时可在Eclipse控制台打印出Cookie信息)。
如果在30秒之内,我们关闭浏览器又访问CookieServlet02,按理可以获取并打印这个Cookie:
如果超过30秒后,我们再次访问CookieServlet02,我们再看控制台:
负数和0的取值,也是同样验证步骤。
2.3 cookie能不能存中文?
在tomcat 8之前,不能存中文。
那怎么办? 需要将中文数据转码 —— 一般采用URL编码(如:%E3)
在tomcat 8之后 ,cookie支持中文数据,但还是不支持特殊字符。
我们现在使用的是tomcat 9,故tomcat8之前的不做演示。
运行服务器,访问CookieServlet03,再访问CookieServlet02打印:
以上验证,tomcat8以上,Cookie能存中文。
2.4 cookie获取范围多大?
假设在一个服务器(也就是一个tomcat)中,部署了多个web项目,那么在这些web项目中cookie能不能共享?

经过作者的超绝不经意安排,有以下两个项目:
它们的cookieDemo2和cookieServlet02都是获取request的Cookies并打印。
这时我们验证的问题:项目1下的cookieServlet01发送给客户端的Cookie,会被项目2下的cookieDemo2获取到吗(即跨项目的cookie能获得吗)

我们接下来就访问这个Servlet:
接着我们访问项目2下的cookieDemo2:
最后得到验证:
假设在一个服务器中,部署了多个web项目,那么在这些web项目中cookie能不能共享?
默认情况下cookie不能共享
假如就有这个需求,需要一个服务器上多个项目共享Cookie,该如何呢?
2.4.2 多个项目共享Cookie
之所以Cookie多个项目不能共享,是因为默认情况下,客户端存储的Cookie目录是以项目名为目录的:
项目不同,项目名就不同。自然该目录下就不会出现其他项目名的Cookie。
这里引入Cookie的另一个方法:
Cookie.setPath(String Path);
//参数为字符串类型,表示该cookie的存储路径。
运行:
此时若我们再访问另一个项目下的打印Servlet:
再看控制台,成功打印了项目1刚刚存的Cookie。
所以,想要实现一个服务器下多个项目下的Cookie共享:就setPath()。
2.4.3 多个服务器Cookie共享
其实还是同样的道理,调用cookie方法,但是是不同的方法:
setDomain(String path)
// 参数:字符串类型,设置域名
如果设置一级域名相同,那么多个服务器之间的cookie可以共享
如果SetDomain(“.baidu.com”),那么tieba.baidu.com和news.baidu.com中cookie可以共享
因为这两个服务器都是baidu管辖下的,那何必要设置两个服务器呢?是因为如果一个服务器承受不了大量的用户请求访问。
比较浅显的解释,但这里简单提出,仅为知识体系的完整。后面再次接触的时候就会深入应用。
三、Cookie的特点和作用:
特点:
1. cookie存储数据在客户端浏览器
2. 浏览器对于单个cookie的大小有限制(4kb左右) 以及 对于同一个域名下的总cookie数量也有限制(20个)
这Cookie的名字也暗示了存不了多少数据。Cookie——“小甜点”,饭后甜食或者下午茶小点心,不能当主食。
作用:
1. Cookie一般用于存储少量的不太敏感的数据
2. 在不登录的情况下,完成服务器对客户端的身份识别;而登录的话,就是在数据库里获取数据
“不太敏感”,存在客户端,咱们一抓包或者在“开发者工具”里就能看到,服务器这边还能删除……无不昭示着客户端安全性不太高,尽量不存敏感数据。
第二点还挺重要,需要好好理解:
我们来到baidu的主页面:
我们在输入框输入问题:
如果我们既不登录,又想实现输入问题的时候没有自动联想:
我们再次来到主页面,此时还是未登录状态:
若我关闭浏览器,再次进入baidu,输入问题也没有自动联想。为什么呢?是因为刚刚的“保存设置”时发出请求,baidu响应了这些设置,数据以set-Cookie标头响应过来。
浏览器保存这些信息(Cookie),而我们再次来到baidu主页面:我们的请求会自动携带Cookie。而baidu服务器则会获取请求里的Cookie,再将我们的需求响应给我们。
这就是Cookie:在未登录的状态下 ,服务器完成对客户端身份的识别。如果登录的话,服务器就直接在它的数据库里提取数据了。
下一节:Cookie的实战应用