10 Cookie
Cookie
标签(空格分隔): JavaWeb
引出问题
我们知道Http是一种无状态的协议,浏览器的每一次请求都是完全孤立的。因此WEB服务器不能够识别出哪些请求是同一个浏览器所发送的。
但是有时候我们需要知道哪些请求是同一个浏览器所发送的很多请求,例如:在电商网站上,我们添加一次购物车是通过一次请求,但是我们在添加下一个商品的时候还要添加这个购物车内,负责处理购物请求的服务器程序必须知道处理上一次请求的程序所得到的用户信息。
作为 web 服务器,必须能够采用一种机制来唯一地标识一个用户,同时记录该用户的状态。
会话和会话状态
WEB应用中的会话是指一个客户端浏览器与WEB服务器之间连续发生的一系列请求和响应过程。
WEB应用的会话状态是指WEB服务器与浏览器在会话过程中产生的状态信息,借助会话状态,WEB服务器能够把属于同一会话中的一系列的请求和响应过程关联起来。
WEB服务器端程序要能从大量的请求消息中区分出哪些请求消息属于同一个会话,即能识别出来自同一个浏览器的访问请求,这需要浏览器对其发出的每个请求消息都进行标识:属于同一个会话中的请求消息都附带同样的标识号,而属于不同会话的请求消息总是附带不同的标识号,这个标识号就称之为会话ID(SessionID)。
在 Servlet 规范中,常用以下两种机制完成会话跟踪:Cookie、Session
Cookie机制
什么Cookie
Cookie 可以翻译为“小甜品,小饼干” ,Cookie 在网络系统中几乎无处不在,当我们浏览以前访问过的网站时,网页中可能会出现 :你好 XXX,这会让我们感觉很亲切,就好像吃了一个小甜品一样。
这其实是通过访问主机中的一个文件来实现在客户端保持 HTTP 状态信息的,这个文件就是 Cookie。在 Internet 中, 浏览器访问WEB服务器的某个资源时,由WEB服务器在HTTP响应消息头中附带传送给浏览器的一个小文本文件。
一旦WEB浏览器保存了某个Cookie,那么它在以后每次访问该WEB服务器时,都会在HTTP请求头中将这个Cookie回传给WEB服务器。
实现原理
Cookie底层的实现原理:
WEB服务器通过在HTTP响应消息中增加Set-Cookie响应头字段将Cookie信息发送给浏览器,浏览器则通过在HTTP请求消息中增加Cookie请求头字段将Cookie回传给WEB服务器。
一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
使用Cookie
API方法
Servlet API中提供了一个javax.servlet.http.Cookie类来封装Cookie信息,它包含有生成Cookie信息和提取Cookie信息的各个属性的方法。
Cookie类的方法:
构造方法: public Cookie(String name,String value)
getName方法
setValue与getValue方法
setMaxAge与getMaxAge方法
setPath与getPath方法
响应:
HttpServletResponse接口中定义了一个addCookie方法,它用于在发送给浏览器的HTTP响应消息中增加一个Set-Cookie响应头字段。
请求:
HttpServletRequest接口中定义了一个getCookies方法,它用于从HTTP请求消息的Cookie请求头字段中读取所有的Cookie项。
Cookie的发送
Cookie的发送分为3步:
1.创建Cookie对象
Cookie cookie = new Cookie("name","Japson");
2.设置最大时效:以秒为单位,若为0,则表示立即删除该cookie,若为负数,表示不存储该cookie,若为整数表示该cookie的存储时间
cookie.setMaxAge(30);
3.将Cookie放入到HTTP响应报头
response.addCookie(cookie);
如果创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie,存储在浏览器的内存中,用户退出浏览器之后被删除。
若希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie。
发送cookie需要使用HttpServletResponse的addCookie方法,将cookie插入到一个 Set-Cookie HTTP响应报头中。由于这个方法并不修改任何之前指定的Set-Cookie报头,而是创建新的报头,因此将这个方法称为是addCookie,而非setCookie。
会话cookie和持久cookie的区别
如果不通过setMaxAge()
设置过期时间,则表示这个cookie生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览器会话期的cookie被称为会话cookie。会话cookie一般不保存在硬盘上而是保存在内存里。
如果通过setMaxAge()
设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie依然有效直到超过设定的过期时间。
存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存的cookie,不同的浏览器有不同的处理方式。
Cookie的读取
Cookie的读取分为两步:
- 调用
request.getCookies()
方法
要获取浏览器发送来的cookie,需要调用HttpServletRequest
的getCookies()
方法,这个调用返回Cookie对象的数组,对应由HTTP请求中Cookie报头输入的值。
Cookie[] cookie = rquest.getCookies();
- 对数组进行循环,调用每个cookie的getName方法,直到找到感兴趣的cookie为止
案例:显示最近浏览的 5 本书的 title
要求
有一个books.jsp页面,上面有很多书名。点击一个书名,跳转到相应的book.jsp页面,该页面显示点击的书名。点击返回,在books.jsp上显示最近浏览的 5 本书的名
books.jsp
如何显示最近浏览的 5 本书:
- 获取所有的 Cookie
- 从中筛选出 Book 的 Cookie:
- 如果 cookieName 为 BOOK_ 开头的即符合条件
- 显示 cookieValue
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h4>Books Page</h4>
<a href="book.jsp?book=JavaWeb">JavaWeb</a><br><br>
<a href="book.jsp?book=Java">Java</a><br><br>
<a href="book.jsp?book=Oracle">Oracle</a><br><br>
<a href="book.jsp?book=Ajax">Ajax</a><br><br>
<a href="book.jsp?book=JavaScrip">JavaScrip</a><br><br>
<a href="book.jsp?book=Spring">Spring</a><br><br>
<a href="book.jsp?book=MyBatis">MyBatis</a><br><br>
<a href="book.jsp?book=MySQL">MySQL</a><br><br>
<br><br>
<h5>HISTORY:</h5>
<br><br>
<%
Cookie[] cookies = request.getCookies();
for (Cookie c : cookies) {
if (c.getName().startsWith("Book_")) {
out.print(c.getValue() + " ");
}
}
%>
</body>
</html>
book.jsp
把书的信息以 Cookie 方式传回给浏览器,删除一个 Cookie:
- 确定要被删除的 Cookie: BOOK_ 开头的 Cookie 数量大于或等于 5,且若从 books.jsp 页面传入的 book 不在 BOOK_ 的 Cookie 中,则删除较早的那个 Cookie( BOOK_ 数组的第一个 Cbookie);若在其中,则删除该 Cookie
- 把从 books.jsp 传入的 book 作为一个 Cookie 返回
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h4>Book Detail Page</h4>
Book: <%= request.getParameter("book")%>
<br><br>
<a href="books.jsp">return</a>
<%
String book = request.getParameter("book");
//把书的信息以Cookie的方式传回给浏览器,删除一个Cookie
//1. 确定要删除的Cookie
//获取Cookie数组
Cookie[] cookies = request.getCookies();
//用来存放与book.jsp传入的book相同的那个Cookie
Cookie tempCookie = null;
//用来存放Book_的Cookie
List<Cookie> list = new ArrayList<>();
if (cookies != null && cookies.length > 0) {
for (Cookie c : cookies) {
if (c.getName().startsWith("Book_")) {
list.add(c);
if (c.getValue().equals(book)) {
tempCookie = c;
}
}
}
}
//如果比5个多,且当前的cookie不再list中,删除第一个;否则删除tempCookie本身
if (list.size() >= 5 && tempCookie == null) {
tempCookie = list.get(0);
}
if (tempCookie != null) {
tempCookie.setMaxAge(0);
response.addCookie(tempCookie);
}
//2. 把books.jsp传入的book作为一个Cookie返回
Cookie cookie = new Cookie("Book_" + book,book);
response.addCookie(cookie);
System.out.println(list.size());
for (Cookie c : list) {
System.out.println(c.getValue());
}
%>
</body>
</html>
cookie.setPath()的用法
Cookie的作用范围:可以作用在当前目录和当前目录的子目录,但不能作用域当前目录的上一级目录。
可以通过设置Cookie的作用范围,其中/
代表站点的根目录,request.getContextPath()
为当前web应用的目录
cookie.setPath("/");
cookie.setPath(request.getContextPath());