Cookie技术详解

1. Cookie的特性

属性:
    1> name: Cookie的名字
    2> value: Cookie的值
    3> path: 可选,Cookie的存储路径,默认情况下的存储路径时访问的Servlet所在的路径
    4> MaxAge: 可选,最大的存活时间,默认情况下是存放在缓存区中的,生命周期是一个会话
    5> version: Cookie的版本
    6> domain: 域名(即网站)
    7> Comment: 备注

下面我带大家来解读读J2EE1.5_API文档中对Cookie技术的说明

下面是我对文档的一点翻译(不准确的之处敬请指正):

Creates a cookie, a small amount of information sent by a servlet to a Web browser, saved by the browser, and later sent back to the server. A cookie's value can uniquely identify a client, so cookies are commonly used for session management.

创建一个cookie,servlet容器向浏览器发送一小段的信息,浏览器会存储这些信息,以后浏览器可以把这些信息发送回给服务器。一个cookie的值可以唯一的表示一个客户端,所以cookies通常被用来作为会话管理。

A cookie has a name, a single value, and optional attributes such as a comment, path and domain qualifiers, a maximum age, and a version number. Some Web browsers have bugs in how they handle the optional attributes, so use them sparingly to improve the interoperability of your servlets.

一个cookie包含一个名字、一个值、类似备注的可选属性、路径、域名、生命值和版本号。一些浏览器在处理可选属性的时候存在缺陷,所以要谨慎的使用它们来提高你的服务器的交互性(健壮性)。

The servlet sends cookies to the browser by using the HttpServletResponse.addCookie(javax.servlet.http.Cookie) method, which adds fields to HTTP response headers to send cookies to the browser, one at a time. The browser is expected to support 20 cookies for each Web server, 300 cookies total, and may limit cookie size to 4 KB each.

servlet容器通过 HttpServletResponse.addCookie(javax.servlet.http.Cookie) 这个方法向浏览器发送cookies,将字段添加到HTTP响应头来向浏览器发送 cookies,一次发送一个cookie。对于每个web服务器,浏览器支持20个cookies,浏览器一共可以支持300个cookies,可能会限制每个cookie的大小为4B.

The browser returns cookies to the servlet by adding fields to HTTP request headers. Cookies can be retrieved from a request by using the HttpServletRequest.getCookies() method. Several cookies might have the same name but different path attributes.

浏览器通过向HTTP请求头添加字段的方式把cookies返回给servlet容器。一个请求可以通过 HttpServletRequest.getCookies() 方法检索到cookies。一些cookies也许有相同的名字,但它们的属性却不相同。

Cookies affect the caching of the Web pages that use them. HTTP 1.0 does not cache pages that use cookies created with this class. This class does not support the cache control defined with HTTP 1.1.

使用Cookies时会影响网页的缓存。HTTP1.0中使用cookie创建的类不缓存页面。在HTTP1.1中cookie这个类不支持缓存控制。

This class supports both the Version 0 (by Netscape) and Version 1 (by RFC 2109) cookie specifications. By default, cookies are created using Version 0 to ensure the best interoperability.

这个类同时支持版本0(Netscape)和版本1(RFC 2109) cookie规范。默认情况下,使用版本0创建cookie,以确保最佳的互操作性。

2. 服务器发送Cookie给浏览器

HttpServletResponse.addCookie(javax.servlet.http.Cookie)

每个网站最多支持20个Cookie,总共支持最多300个,每个Cookie最大4kb. (Cookie的资源是非常稀少的,Cookie中存储的数据都是非常必要的重要的数据。)

3. 服务端如何获得浏览器所携带的Cookie

HttpServletRequest.getCookies()

4. 服务端如何删除Cookie

由于服务端没有提供删除的方法,因此采用创建相同的Cookie,设置存活时间为0的办法,覆盖已经存在的Cookie

5. 如何唯一的确定一个Cookie

域名+ 访问路径 + Cookie的名字

6. 浏览器到底带不带Cookie,谁说了算呢?

浏览器到底带不带Cookie是由浏览器说了算。

浏览器通过判断你在地址栏中敲入的资源地址. starWith(在硬盘上存储的Cookie里面存储的路径) 为true ,那就带,否则就不带.

7. 演示代码

演示了记录上次访问的时间

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8") ;
		response.setContentType("text/html;charset=UTF-8") ;
		PrintWriter out = response.getWriter() ;
		
		out.write("您上次访问的时间是: ") ;
		//拿到客户端携带的记录上次访问时间的Cookie : 假设Cookie的名字是lastaccesstime , 值是一个long类型的数字
		//拿到客户端携带的所有的Cookie
		Cookie[] cs = request.getCookies() ;
		//循环判断拿到我们需要的Cookie
		for (int i = 0; cs!=null && i < cs.length; i++) {
			Cookie c = cs[i] ;
			if(c.getName().equals("lastaccesstime")){
				//说明找到了需要的Cookie
				String time = c.getValue() ;
				//将time转换成long类型的值
				long t = Long.parseLong(time) ;
				//格式化为我们需要的格式
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss") ;
				//创建一个date对象
				Date d = new Date(t) ;
				//将数据输出到页面上
				out.write(sdf.format(d) + "&nbsp;&nbsp;<a href = '"+ request.getContextPath()+"/servlet/ServletCookie2'>清除Cookie</a>") ;
			}
		}
		
		//向客户端发送Cookie
		Cookie c = new Cookie("lastaccesstime",new Date().getTime() + "") ;
		
		//设置Cookie的存活时间(只要设置了存活时间,那么此Cookie就将存储到客户端的硬盘上,不会在缓存中存储)
		c.setMaxAge(Integer.MAX_VALUE) ;
		
		//设置浏览器携带Cookie的路径
		c.setPath(request.getContextPath()) ;
		
		//设置路径如果是/,那就意味着你访问服务器上的任意工程资源都会携带此Cookie
		//c.setPath("/") ;  //设置路径是服务器的根路径    协议 + 主机名 + 端口号
		
		//发送到客户端
		response.addCookie(c) ;
	}
}

删除客户端存储的Cookie

public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		//要想删除客户端存储的Cookie,sun公司没有提供相应的删除方法,
		//所以采用的办法就是创建一个同名的cookie,将存活时间设置为0,然后覆盖客户端存储的Cookie
		
		Cookie c = new Cookie("lastaccesstime","") ;
		c.setMaxAge(0) ;
		
		//设置路径
		c.setPath(request.getContextPath()) ;
		
		//发送到客户端
		response.addCookie(c) ;
		
	}

演示是否能够获取Cookie(演示path的含义)

public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8") ;
		response.setContentType("text/html;charset=UTF-8") ;
		PrintWriter out = response.getWriter() ;
		
		out.write("你上次访问的时间是") ;
		//拿到客户端所有的Cookie
		Cookie[] cs = request.getCookies() ;
		//拿到记录上次访问时间的Cookie
		for (int i = 0;cs!= null && i < cs.length; i++) {
			Cookie c = cs[i] ;
			if(c.getName().equals("lastaccesstime")){
				String value = c.getValue() ;
				
				out.write(value) ;
			}
		}
	}

8. 记录用户名和密码案例

1> 创建登录页面

public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		request.setCharacterEncoding("UTF-8") ;
		response.setContentType("text/html;charset=UTF-8") ;
		PrintWriter out = response.getWriter() ;
		
		//拿到错误信息
		String name = (String) request.getAttribute("error") ;
		if(name != null)
			 out.write("<font color = red>" + name + "</font>") ;
		
		String username = "" ;
		String pass = "" ;
		//拿到客户端携带的所有的Cookie
		Cookie[] cs = request.getCookies() ;
		//循环判断
		for (int i = 0; cs !=null && i < cs.length; i++) {
				Cookie c  = cs[i] ;
				if(c.getName().equals("name")){
					//说明找到了存储用户名的cookie
					username = c.getValue() ;
				}
				if(c.getName().equals("pass")){
					//说明找到了存储密码的Cookie
					pass = c.getValue() ;
				}
		}
		
		//创建登陆页面
		out.write("<form action = '" + request.getContextPath()+"/servlet/LoginServlet' method = 'post'>") ;
		out.write("姓名:<input type = 'text' name = 'username' value = '" + username + "'><br>") ;
		out.write("密码:<input type = 'text' name = 'password' value = '" + pass + "'><br>") ;
		out.write("<input type = 'checkbox' name = 'remeber' value = 'on'>记住用户名及密码两周<br>") ;
		out.write("<input type = 'submit' value = '登陆'><br>") ;
		
		
	}

2> 登录的逻辑判断

//向客户端发送清除或者记录用户名和密码的Cookie
public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8") ;
		response.setContentType("text/html;charset=UTF-8") ;
		PrintWriter out = response.getWriter() ;
		
		//拿取浏览器器传递的数据
		String name = request.getParameter("username") ;
		String pass = request.getParameter("password") ;
		String remeber =request.getParameter("remeber") ;
		
		//判断用户是否是合法用户 : 假定name和pass的逆序一样就是合法用户
		//拿到密码的逆序
		String pass1 = new StringBuffer(pass).reverse().toString() ;
		
		//判断
		if(name.equals(pass1)){
			//是合法用户
			//判断用户是否选择了记住用户名和密码
			//说明用户选择了记录
			Cookie c  = new Cookie("name",name) ;
			Cookie c1  = new Cookie("pass",pass) ;
			if("on".equals(remeber)){
				//设定存储到客户端的硬盘上
				c.setMaxAge(Integer.MAX_VALUE) ;
				c1.setMaxAge(Integer.MAX_VALUE) ;
				
			}else{
				//设定客户端存储的用户名和密码立刻失效
				c.setMaxAge(0) ;
				c1.setMaxAge(0) ;
			}
			//设定访问路径
			c.setPath(request.getContextPath()) ;
			c1.setPath(request.getContextPath()) ;
			
			//向客户端发送Cookie
			response.addCookie(c) ;
			response.addCookie(c1) ;
			
			request.setAttribute("name", name) ;
			request.getRequestDispatcher("MainServlet").forward(request, response) ;
		}else{
			//非法用户
			request.setAttribute("error", "用户名或者密码错误") ;
			request.getRequestDispatcher("ServletUI").forward(request, response) ;
		}
	}

3> 登录成功的页面

public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8") ;
		response.setContentType("text/html;charset=UTF-8") ;
		PrintWriter out = response.getWriter() ;
		
		String name = (String) request.getAttribute("name") ;
		
		out.write(name + ",欢迎你") ;
		
	}

9. Cookie记住历史浏览记录

1> 创建Book模型

public class Book {

	private String id ;
	
	private String bookName ;
	
	private String author ;
	
	private float price ;
	
	private String description ;
	
	public Book(String id, String bookName, String author, float price,
			String description) {
		this.id = id;
		this.bookName = bookName;
		this.author = author;
		this.price = price;
		this.description = description;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getBookName() {
		return bookName;
	}

	public void setBookName(String bookName) {
		this.bookName = bookName;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public float getPrice() {
		return price;
	}

	public void setPrice(float price) {
		this.price = price;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	@Override
	public String toString() {
		return "Book [id=" + id + ", bookName=" + bookName + ", author="
				+ author + ", price=" + price + ", description=" + description
				+ "]";
	}
}

2> 创建Book的工具类

public class BookUtils {

	private static Map<String,Book> map = new HashMap<String,Book>() ;
	
	// 静态块,当加载此静态块的时候,书这个类就已经在里面了,模拟数据库中的数据
	static{
		map.put("1", new Book("1","葵花宝典","安倍晋三",100,"欲练神功,必须先练好基本功")) ;
		map.put("2", new Book("2","辟邪剑谱","陈冠希",80,"绝世好书")) ;
		map.put("3", new Book("3","西游记","吴承恩",50,"一群小猴子的故事")) ;
		map.put("4", new Book("4","水浒传","施耐庵",90,"三个女人和105个男人的故事")) ;
		map.put("5", new Book("5","西厢记","阿娇",70,"好好看啊。。。。。。")) ;
		map.put("6", new Book("6","神雕侠侣","金庸",100,"感天动地的旷世绝恋")) ;
		map.put("7", new Book("7","红楼梦","葫芦娃",60,"男人的梦想。。。。。。。")) ;
	}
	// 获取所有的书
	public  static Map<String,Book> getAllBook(){
		return map ;
	}
	
	// 根据书的id获取谋一本书
	public static Book getBookById(String id){
		return map.get(id) ;
	}
}

3> 显示所有的书

public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		request.setCharacterEncoding("UTF-8") ;
		response.setContentType("text/html;charset=UTF-8") ;
		PrintWriter out = response.getWriter() ;
		
		out.write("本站有以下好书:<br>") ;
		//1.显示所有的书
		//获取所有的书的集合
		Map<String ,Book> map = BookUtils.getAllBook() ;
		//遍历集合
		for (Map.Entry<String, Book> entry : map.entrySet()) {
			//拿到每一本的id
			String id = entry.getKey() ;
			//拿到每一本书
			Book book = entry.getValue() ;
			//输出书的名字
			out.write(book.getBookName() + "&nbsp;&nbsp;<a href = '"+ request.getContextPath()+"/servlet/ShowBookDetailServlet?id=" + id +"'>显示详细信息</a><br>") ;
		}
		out.write("<br><br><br><br>") ;
		//2显示浏览的历史记录: 假设存放历史记录的Cookie的名字叫history : 值的形式: 1-2-3
		//拿到客户端携带的所有的Cookie
		Cookie[] cs = request.getCookies() ;
		//循环判断
		for (int i = 0; cs !=null && i < cs.length; i++) {
			Cookie c = cs[i] ;
			if("history".equals(c.getName())){
				out.write("你的浏览历史记录如下: <br>") ;
				//找到了存放历史记录的Cookie
				String value = c.getValue() ;
				//将字符串拆分成成数组
				String[] ids = value.split("-") ;
				//循环数组,拿到每一本书,显示在页面上
				for (int j = 0; j < ids.length; j++) {
					Book b = BookUtils.getBookById(ids[j]) ;
					out.write(b.getBookName() + "<br>") ;
				}
			}
		}
		
		
	}

4> 显示书的详细信息

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8") ;
		response.setContentType("text/html;charset=UTF-8") ;
		PrintWriter out = response.getWriter() ;
		
		//1.显示书的详细信息
		//拿到页面传递的id
		String id = request.getParameter("id") ;
		//根据id获取书
		Book book = BookUtils.getBookById(id) ;
		//显示信息
		out.write(book + "&nbsp;&nbsp;<a href = '" + request.getContextPath() +"/servlet/ShowAllBookServlet'>返回主页继续浏览</a><br>") ;
		
		//2.发送历史浏览记录
		//功能:获取历史记录字符串
		String history = getHistory(request,id) ;
		//创建Cookie,发送Cookie
		Cookie c = new Cookie("history",history) ;
		//设置Cookie存放到客户端的硬盘上
		c.setMaxAge(Integer.MAX_VALUE) ;
		//设置客户端携带Cookie的路径
		c.setPath(request.getContextPath()) ;
		//发送Cookie
		response.addCookie(c) ;
	}
	
	/**
	 *  浏览器携带的历史记录的Cookie    点击的书的id   最终需要发送的字符串
	 *    1.  无						             1				   1
	 *    2.  1						               1				   1
	 *    3.  1								       2				   2-1
	 *    4.  1-2                                     1				   1-2
	 *    5.  1-2						         	2				   2-1
	 *    6.  1-2						         	3				   3-1-2
	 *    7.  1-2-3							       1			   	1-2-3
	 *    8.  1-2-3						       	2				   2-1-3
	 *    9.  1-2-3						       	3				   3-1-2
	 *    10. 1-2-3						       	4				   4-1-2
	 *  
	 */
	
	//获取要发往客户端的历史记录的字符串
	private String getHistory(HttpServletRequest request,String id) {
		//设定一个Cookie为空
		Cookie history = null ;
		//拿到所有的Cookie
		Cookie[] cs = request.getCookies() ;
		//循环判断给history赋值
		for (int i = 0;cs != null && i < cs.length; i++) {
			if(cs[i].getName().equals("history")){
				history = cs[i] ;
				break ;
			}
		}
		
		//考虑情况1
		if(history == null)
			return id ;
		
		//拿到客户端浏览器携带的历史记录的字符串
		String value = history.getValue() ;
		
		//考虑情况2,3
		if(value.length() == 1){
			if(value.equals(id))
				//说明点击的是浏览过的
				return id ;
			else
				//点击的是新的书
				return id + "-" + value ;
		}
		
		//先将历史记录字符串拆分成数组,将数组中的元素放入集合中
		String[] ids = value.split("-") ;
		LinkedList<String> list = new LinkedList<String>(Arrays.asList(ids)) ;
		
		int index = list.indexOf(id) ;
		//考虑情况4,5,6
		if(value.length() == 3){
			if(index == -1){
				//说明此id没有点击过
				list.addFirst(id) ;
			}else{
				//说明点击的id以前点击过
				list.remove(index) ;
				list.addFirst(id) ;
			}
		}
		
		//考虑情况7,8,,9,10
		if(value.length() > 3){
			if(index == -1){
				//说明点击的id以前没有点击过
				list.removeLast() ;
				list.addFirst(id) ;
			}else{
				//说明点击的id是浏览器中已经携带的有的
				list.remove(index) ;
				list.addFirst(id) ;
			}
		}
		
		//需要将集合中的数据形成需要的字符串
		StringBuffer sb = new StringBuffer(list.get(0)) ;
		for (int i = 1; i < list.size(); i++) {
			sb.append("-" + list.get(i)) ;
		}
		
		return sb.toString();
	}
posted @ 2016-04-22 14:54  Coder-LiLe  阅读(601)  评论(0编辑  收藏  举报