[04] Cookie概念和基本使用
1、Cookie是什么
Cookie,中文名称为“小型文本文件”或“小甜饼”,指某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密)。
很多网站在你浏览后,会在你电脑中留下小小的档案,也就是我们说的Cookie,以便你再次浏览时,网站会读取它上次给你留下的Cookie资料,如果有的话,就可以根据内容来判断使用者,送出特定的网页内容。
因为HTTP协议是无状态的,即服务器不知道用户上一次做了什么,这严重阻碍了交互式Web应用程序的实现。所以Cookie就是用来绕开HTTP的无状态性的“额外手段”之一。
Cookie的一个典型的应用是当登录一个网站时,网站往往会请求用户输入用户名和密码,并且用户可以勾选“下次自动登录”。如果勾选了,那么下次访问同一网站时,用户会发现没输入用户名和密码就已经登录了。这正是因为前一次登录时,服务器发送了包含登录凭据(用户名加密码的某种加密形式)的Cookie到用户的硬盘上。第二次登录时,(如果该Cookie尚未到期)浏览器会发送该Cookie,服务器验证凭据,于是不必输入用户名和密码就让用户登录了。
所以:
- Cookie是保存在客户端的小文本
- 保存位置分两种
- (1)保存在客户端浏览器所占内存中,关闭浏览器后Cookie也消失
- (2)保存在客户端PC机的硬盘上,设置有效时间,超期后失效
但是要注意的是,Cookie既然能把小文本保存在客户端,并在服务器和客户端之间进行传输,那么意味着它也容易造成信息泄露,因此:
- 不用Cookie保存对保密性要求高的信息,如银行卡密码等
- 不用Cookie实现某些必要功能,防止Cookie删除后功能出现错误
- 可以通过浏览器设置阻止Cookie,或手工清除Cookie
- Cookie放在请求头Header里,而不是主体Body中,所以GET或POST方式的请求都可以发送Cookie
另外,Cookie还有一些缺陷:
- Cookie会被附加在每个HTTP请求中,所以无形中增加了流量
- 由于在HTTP请求中的Cookie是明文传递的,所以安全性成问题(除非用HTTPS)
- Cookie的大小限制在4KB左右。对于复杂的存储需求来说是不够用的
如果你想看到自己的Cookie,也打开某个网站,在控制台调用JS代码: javascript:alert("Cookie:"+document.cookie) 或 javascript:document.write(document.cookie)
2、Java中的Cookie类
在Servlet API中,存在Cookie类,可以使用new关键字进行创建:
Cookie cookie = new Cookie("username", "zhangsan");
1
1
Cookie cookie = new Cookie("username", "zhangsan");
可以看到,Cookie对象是保存一对键值对,都是字符串形式。
Cookie类定义了一系列的方法,摘要部分如下:
类型 | 方法名 | 说明 |
void | setMaxAge(int expiry) | 设置Cookie有效期,以秒为单位,保存在硬盘上或内存中
|
void | setValue(String value) | Cookie创建后,对Cookie进行赋值 |
String | getName() | 获取Cookie的名称 |
String | getValue() | 获取Cookie的值 |
String | getMaxAge() | 获取Cookie的有效时间,以秒为单位 |
保存和获取Cookie:
- 把Cookie保存到客户端 response.addCookie(Cookie cookie)
- 获取请求中的Cookies request.getCookies()
Cookie要保存到客户端,凡是写到客户端的方法,基本都是在响应中,HttpServletResponse提供了方法,把Cookie保存到客户端;而再次访问与保存Cookie相同域名的网站时,HTTP协议将把有效时间内的Cookie都发送到服务器,容器将Cookie封装到请求中,HttpServletRequest提供了获取Cookie对象数组的方法。
3、一个示例带你看Cookie
上面单纯文字的说明太过干瘪,那么就来一个生动鲜活的例子:
写登陆页面展示如下:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>登陆页面</title>
</head>
<body>
<%
String username = null;
String password = null;
//获取请求中的所有cookie对象
Cookie[] cookies = request.getCookies();
//只要存在Cookie,就查找是否有用户名和密码
if(cookies != null) {
for(Cookie cookiesTemp : cookies) {
if(cookiesTemp.getName().equals("username")) {
username = cookiesTemp.getValue();
}
if(cookiesTemp.getName().equals("password")) {
password = cookiesTemp.getValue();
}
}
}
if(username != null && password != null) {
request.getRequestDispatcher("LoginServlet?username=" + username + "&pwd=" + password).forward(request, response);
}
%>
<form name="form1" action="LoginServlet" method="post">
用户名:<input type="text" id="username" name="username"><br>
密码:<input type="password" id="pwd" name="pwd"><br>
<select id="timelength" name="timelength">
<option value="0" selected>每次都需要登陆</option>
<option value="3">3天内免登陆</option>
<option value="7">7天内免登陆</option>
</select><br>
<input type="submit" name="submit" value="登陆">
</form>
</body>
</html>
x
1
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
2
3
<html>
4
<head>
5
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6
<title>登陆页面</title>
7
</head>
8
<body>
9
<%
10
String username = null;
11
String password = null;
12
13
//获取请求中的所有cookie对象
14
Cookie[] cookies = request.getCookies();
15
16
//只要存在Cookie,就查找是否有用户名和密码
17
if(cookies != null) {
18
for(Cookie cookiesTemp : cookies) {
19
if(cookiesTemp.getName().equals("username")) {
20
username = cookiesTemp.getValue();
21
}
22
if(cookiesTemp.getName().equals("password")) {
23
password = cookiesTemp.getValue();
24
}
25
}
26
}
27
28
if(username != null && password != null) {
29
request.getRequestDispatcher("LoginServlet?username=" + username + "&pwd=" + password).forward(request, response);
30
}
31
%>
32
33
<form name="form1" action="LoginServlet" method="post">
34
用户名:<input type="text" id="username" name="username"><br>
35
密码:<input type="password" id="pwd" name="pwd"><br>
36
<select id="timelength" name="timelength">
37
<option value="0" selected>每次都需要登陆</option>
38
<option value="3">3天内免登陆</option>
39
<option value="7">7天内免登陆</option>
40
</select><br>
41
<input type="submit" name="submit" value="登陆">
42
</form>
43
44
</body>
45
</html>
写后台Servlet代码如下:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset = utf-8");
//获取用户名、密码
String username = request.getParameter("username");
String password = request.getParameter("pwd");
//获得JSP页面的时间信息
String timelength = request.getParameter("timelength");
int days = 0;
//类型转换
if(timelength != null) {
days = Integer.parseInt(timelength);
}
//只要天数不为0,则创建cookie,设置有效时间,存到客户端
if(days != 0) {
Cookie usernameCookie = new Cookie("username", username);
Cookie passwordCookie = new Cookie("password", password);
usernameCookie.setMaxAge(days * 24 * 3600);
passwordCookie.setMaxAge(days * 24 * 3600);
response.addCookie(usernameCookie);
response.addCookie(passwordCookie);
}
//跳转到home.jsp
request.getRequestDispatcher("home.jsp").forward(request, response);
}
1
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
2
response.setContentType("text/html; charset = utf-8");
3
//获取用户名、密码
4
String username = request.getParameter("username");
5
String password = request.getParameter("pwd");
6
7
//获得JSP页面的时间信息
8
String timelength = request.getParameter("timelength");
9
int days = 0;
10
11
//类型转换
12
if(timelength != null) {
13
days = Integer.parseInt(timelength);
14
}
15
16
//只要天数不为0,则创建cookie,设置有效时间,存到客户端
17
if(days != 0) {
18
Cookie usernameCookie = new Cookie("username", username);
19
Cookie passwordCookie = new Cookie("password", password);
20
usernameCookie.setMaxAge(days * 24 * 3600);
21
passwordCookie.setMaxAge(days * 24 * 3600);
22
response.addCookie(usernameCookie);
23
response.addCookie(passwordCookie);
24
}
25
26
//跳转到home.jsp
27
request.getRequestDispatcher("home.jsp").forward(request, response);
28
}
该示例中,输入账户密码登陆后,Servlet中最后会调用response.addCookie方法,将账号密码存到客户端的硬盘上。
我们就到硬盘里去翻一翻,在这之前要说明的是:
- 不同浏览器的Cookie存放位置不同
- 不同浏览器的Cookie存放文件格式不同,IE采用明文的文本文件,Safari采用二进制文件,Chrome和Firefox采用的是Sqlite数据库文件格式
3.1 IE
为了方便展示,我们用IE浏览器登陆一次,然后可以在浏览器设置中,找到存放Cookie文件的位置:
可以看到,除了刚才登陆的localhost,也还有平时登陆其他网站留下的Cookie,我们看刚才登陆的localhost的Cookie就行了:
可见,确实把我们的账号和密码放到了Cookie中,并发送到了客户端的硬盘里:
3.2 Chrome
Chrome因为文件格式问题,直接在硬盘上打开实际上你看到的基本都是乱码:
但索性我们还是可以直接在浏览器中看到的:
4、Cookie的格式
我们已经知道了Cookie是个小文本,而且也基本是按照key/value的方式存储,所以也才有了封装的Cookie对象用来保存一对键值对。
Cookie的内容当然不止我们定义的那对key/value,实际上它是有一定格式的。Cookie的标准格式定义在不同浏览器中实现,在历史上也有差异。下面是常用的标准格式,或者说属性:
- name 用户可定义的key
- value 用户可定义的value
- comment 用来提供注释说明
- path 指定Cookie作用路径,和Domain配合限制Cookie的作用范围
- domain 指定作用域
- max-age 指定Cookie失效时间
- secure 用来远程发送Cookie时告知浏览器数据已加密,只能HTTPS连接被发送
- httponly 本地JavaScript无法读取Cookie信息
这里再说明一下path和domain,假设写Cookie的程序的访问路径是 http://localhost:8080/JavaWeb/servlet/CookieDemo
- localhost就是域名
- /JavaWeb/servlet就是当前Cookie的path
假设现浏览器存的cookie的路径是/JavaWeb/servlet/,则:
- 访问的地址是:http://localhost:8080/JavaWeb/servlet/CookieDemo 则带该cookie
- 访问的地址是:http://localhost:8080/JavaWeb/CookieDemo 则不带该cookie
也就是说,Cookie有个不可跨域名性,就像访问Google只会带上Google的Cookie,而不会带上Baidu的Cookie。
如下示例图,访问淘宝网时携带的Cookie:
5、最后
因为Cookie实现自动登录涉及到用户信息安全的问题,实际上真正的自动登录不会像上面这么简单粗暴的,从我们查看Cookie就可以知道,账号密码都可以赤裸裸地看到,这显然是不行的。
但是这里不做展开,留下部分参考,以便未来学习: