2.3 Java Servlet 会话管理 使用Cookies

2.3 Cookies

URL重写隐藏域仅适合保存无须跨越太多页面的信息。如果需要在多个页面间传递信息,则以上两种技术实现成本高昂,因为你不得不在每个页面都进行相应处理。幸运的是,Cookies技术可以帮助我们。
Cookies是一个很少的信息片段,可自动地在浏览器和Web服务器间交互,因此cookies可存储在多个页面间传递的信息。Cookie作为HTTP header的一部分,其传输由HTTP协议控制。此外,你可以控制cookies的有效时间。浏览器通常支持每个网站高达20个cookies
Cookies的问题在于用户可以通过改变其浏览器设置来拒绝接受cookies
要使用cookies,需要熟悉javax.servlet.http.Cookie 类以及HttpServletRequestHttpServletResponse两个接口。

创建Cookies

可以通过传递namevalue两个参数给Cookie 类的构造函数来创建一个cookies

1
Cookie cookie = new Cookie(name, value);

如下是一个创建语言选择的cookie示例:

1
Cookie languageSelectionCookie = new Cookie("language", "Italian");

Cookie属性

创建完一个Cookie对象后,你可以设置domainpathmaxAge属性。其中,maxAge 属性决定cookie何时过期

服务器如何发送Cookie

要将cookie发送到浏览器,需要调用 HttpServletResponseadd方法:

1
httpServletResponse.addCookie(cookie);

浏览器如何发送Cookie

浏览器在访问同一Web服务器时,会将之前收到的 cookie一并发送。 此外,Cookies也可以通过客户端的javascript脚本创建和删除。

服务器端如何读取浏览器提交的Cookie

服务端若要读取浏览器提交的cookie,可以通过 HttpServletRequest接口的getCookies方法,该方法返回一个Cookie数组,若没有Cookies则返回null.

查找Cookie

你需要遍历整个数组来查询某个特定名称的cookie。如下为查询名为maxRecordscookie的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//获取浏览器提交的所有Cookie数组
Cookie[] cookies = request.getCookies();
//引用,用来保存你想要得到的某个cookie对象
Cookie maxRecordsCookie = null;
if (cookies != null) {
for (Cookie cookie : cookies)
{
//查找名称为maxRecords的cookie对象
if (cookie.getName().equals("maxRecords"))
{
//保存找到的这个cookie对象
maxRecordsCookie = cookie;
break;
}
}
}

目前,还没有类似于getCookieByName这样的方法来帮助简化工作。

删除Cookie

没有一个直接的方法来删除一个cookie,你只能创建一个同名的Cookie,并将maxAge属性设置为0,并添加到HttpServletResponse接口中,覆盖掉原来的Cookie即可。如下为删除一个名为userNamecookie代码:

1
2
3
Cookie cookie = new Cookie("userName", "");
cookie.setMaxAge(0);
response.addCookie(cookie);

demo

工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package session.management.cookies;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
public class CookieTools
{
/**
* 从浏览器提交的Cookie中找到名称为name
* 的Cookie对象.
*
* @param request
* 请求.
* @param name
* 要查找的Cookie的名称.
* @return Cookie对象,如果找到该对象,
* 则返回该对象,没找到则返回null.
*/
public static Cookie findCookieByName(
HttpServletRequest request,String name)
{
Cookie cookies[] = request.getCookies();
for (Cookie cookie : cookies) {
if (cookie.getName().equals(name)) {
return cookie;
}
}
return null;
}
}

设置Cookie的Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package session.management.cookies;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(
name = "SetCookieByForm",
urlPatterns =
{"/setcookie"}
)
public class SetCookieByForm extends HttpServlet
{
private static final long serialVersionUID = 1L;
// 设置一个菜单栏
public static final String MENU =
"<div style='background:#e8e8e8;"
+ "padding:15px'>"
+ "<a href='setcookie'>Set Cookie</a>&nbsp;&nbsp;"
+ "<a href='showfruits'>Show Fruits</a>&nbsp;&nbsp;"
+ "<a href='showcss'>Show CSS</a>&nbsp;&nbsp;"
+ "</div>";
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException
{
// 以UTF-8编码响应
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.print("<html>\r\n"
+ "<head>\r\n"
+ "<title>Preference</title>\r\n"
+ "</head>\r\n"
+ "<body>"
+ MENU
+ "<form method=\"post\">\r\n"
+ "<hr>\r\n"
+ "<table>\r\n"
+ "<tbody>\r\n"
+ "<tr>\r\n"
+ "<td>设置字体大小: </td>\r\n"
+ "<td>\r\n"
+ "<select name=\"cookieFontSize\">\r\n"
+ "<option>large</option>\r\n"
+ "<option>x-large</option>\r\n"
+ "<option>xx-large</option>\r\n"
+ "</select>\r\n"
+ "</td>\r\n"
+ "</tr>\r\n"
+ "</table><hr><table>\r\n"
+ "<tbody>\r\n"
+ "<tr>\r\n"
+ "<td>显示的水果数量:</td>\r\n"
+ "<td>\r\n"
+ "<select name=\"fruitNum\">\r\n");
for (int i = 0;
i < ShowFruitsByCookie.fruits.length;
i++)
{
writer.print(
"<option>" + (i + 1)
+ "</option>\r\n");
}
writer.print(
"</select>\r\n"
+ "</td>\r\n"
+ "</tr>\r\n"
+ "<tr>\r\n"
+ "<td rowspan=\"2\">\r\n"
+ "<input type=\"submit\" value=\"Set\">\r\n"
+ "</td>\r\n"
+ "</tr>\r\n"
+ "</tbody>\r\n"
+ "</table>\r\n"
+ "</form>\r\n"
+ "</body>\r\n"
+ "</html>");
}
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// 以utf-8发送响应
response.setCharacterEncoding("utf-8");
// 读取表单提交的值
String fruitNum =
request.getParameter("fruitNum");
// 创建一个Cookie,设置为用户在表单中输入的值,
// 然后发送给浏览器
response.addCookie(
new Cookie("fruitNum", fruitNum));
// 获取表单提交的值
String cookieFontSize =
request.getParameter("cookieFontSize");
// 存储到Cookie中.
response.addCookie(
new Cookie("cookieFontSize", cookieFontSize));
// 响应post请求
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.println(
"<html><head>"
+ "<title>Preference</title>"
+ "</head><body>"
+ MENU
+ "<ul><li>你想要显示 <strong>"
+ fruitNum
+ "</strong> 种水果。</li>"
+ "<li>你想要设置CSS选择器<strong>"
+".cookieStyle</strong>的字体大小为:<strong>"
+ cookieFontSize
+ "</strong></li></ul></body></html>");
}
}

这个Servlet会把表单输入转换为Cookie发送到浏览器,对应的URL为http://localhost:8080/SessionManagement/setcookie,
当浏览器再次访问http://localhost:8080/SessionManagement/这个Java项目下的资源时,浏览器会把之前服务器发给他的Cookie,再原封不动的提交给服务器。这样服务器就可以根据Cookie来进行响应的操作。
在上面代码中,服务器分别发送fruitNumcookieFontSize这两个cookie给浏览器,下面将再创建两个Servlet来使用者连个Cookie。
填写表单,如下所示:
这里有一张图片
然后点击Set按钮,提交表单,得到提示如下:
这里有一张图片

根据Cookie显示数组中的元素

ShowFruitsByCookie这个Servlet会根据fruitNum这个Cookie的值,来显示打印数组中的元素,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package session.management.cookies;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(
name = "ShowFruitByCookie",
urlPatterns =
{"/showfruits"}
)
public class ShowFruitsByCookie extends HttpServlet
{
private static final long serialVersionUID =
7275643908439277071L;
static String[] fruits = {
"苹果", "葡萄", "芒果", "草莓",
"荔枝", "香蕉", "乌梅", "菠萝",
"樱桃", "枇杷", "芒果", "橙子",
"桔子", "柠檬", "柚子", "杨梅",
"木瓜", "桂圆", "榴莲", "西瓜",
"石榴", "山楂", "椰子", "山竹",
"橄榄", "柿子","李子", "桑葚",
"红枣", "柑橘", "话梅", "乌梅",
"红提", "桃子", "甜瓜", "香瓜",
"荸荠","龙眼", "沙果", "佛手",
"芭乐", "蓝莓", "西梅", "释迦",
"黄皮", "莲雾", "杏子", "槟榔",
"酸橙", "黑莓", "栗子", "板栗",
"金桔", "山梨", "毛桃", "白果",
"银杏", "青梅", "蜜桃","脐橙",
"沙枣", "凤梨", "椰枣", "油桃",
"鳄梨", "酸莓", "蛇果", "鸭梨"};
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
Cookie fruitNumCookie =
CookieTools.findCookieByName(request, "fruitNum");
if (fruitNumCookie != null) {
// 取出Cookie中的值.
String cookieValue = fruitNumCookie.getValue();
int fruitNum = Integer.parseInt(cookieValue);
if (fruitNum <= fruits.length) {
response.setCharacterEncoding("utf-8");
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.print(
"<html>\r\n"
+ "<head>\r\n"
+ "<title>showFruits</title>\r\n"
+ "</head>\r\n"
+ "<body>\r\n"
+ SetCookieByForm.MENU
+ "为你显示 <strong>"
+ fruitNum
+ "</strong> 种水果,如下所示:\r\n"
+ "<ul>");
for (int i = 0; i < fruitNum; i++) {
writer.print("<li>" + fruits[i] + "</li>");
}
writer.println("</ul>\r\n"
+ "</body>\r\n" + "</html>");
}
}
}
}

点击工具条上的Show Fruits 超链接,效果如下:
这里有一张图片

根据Cookie设置CSS

SetCSSByCookie这个Servlet根据cookieFontSize这个Cookie中的值来来设置元素的CSS样式,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package session.management.cookies;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(
name = "SetCSSByCookie",
urlPatterns =
{"/showcss"}
)
public class SetCSSByCookie extends HttpServlet
{
private static final long serialVersionUID =
-8190275536448562340L;
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
response.setCharacterEncoding("utf-8");
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
Cookie cookieFontSize =
CookieTools.findCookieByName(request,
"cookieFontSize");
writer.print(
"<html>\r\n"
+ "<head>\r\n"
+ "<title>根据Cookie设置CSS</title>\r\n"
+ "<style>");
writer.print(".cookieStyle{");
if (cookieFontSize != null) {
writer.print("font-size: "
+ cookieFontSize.getValue() + ";");
}
writer.print("}");
writer.println(
"</style>\r\n"
+ "</head>\r\n"
+ "<body>"
+ SetCookieByForm.MENU);
writer.println(
"<div class=\"cookieStyle\">这句话的样式通过Cookie设置.</div>");
writer.println("</body>\r\n"
+ "</html>");
}
}

点击工具条上的Show CSS超链接,显示效果如下:
这里有一张图片