week 5 session and cookie
一、概念
为什么需要cookie:
1、因为HTTP是无状态的协议,所以我们在使用浏览器访问网站的时候,服务器是怎样来区别是哪个浏览器发送的请求呢,服务器是如何给不同的浏览器发送不同的信息,这就需要我们的cookie来解决问题了。
2、cookie机制是采用了一种在客户端保存状态的方案,他是用户端的存储机制,当然他也需要用户端主动去开启cookie支持。
3、session机制是采用一种在客户端和服务器之间保持状态的方案,这是怎样的一种感念呢?如果我们在服务器端保存一个用户访问的标识,那么这种单一的保持我们还是无法辨别这是哪个客户端发来的请求,所以我们仍然需要在客户端保存一个相同服务器端的标识,所以session机制还需要借助cookie机制来达到他的目的。
cookie和session的工作原理:
cookie:当用户访问到网站时,用户获得不仅仅是这些页面,同时也获取了cookie,并且能够保存在本地磁盘中,在cookie中记录了每次用户访问站点的日期和时间信息或者是用户名密码等信息,用户在和服务器进行请求响应的同时也在不断交换其产生的cookie,并且用户会将最新的cookie存储在磁盘中,对于不同的网站,客户端存储不同的cookie信息。如果用户在关闭掉浏览器后,再次请求相同的网站信息,浏览器便会在本地磁盘上查询是否有于该URL相关联的cookie,如果有那么浏览器便将这个cookie和你的请求信息一同发送给服务器。从上面的工作流程中我们可以发现,cookie是和站点相关联的客户端保存信息的机制。只要你访问这个站点,就会用到你刚才保存的cookie。
session:当程序需要为某个客户端的请求创建一个session的时候,服务器会首先检查这个客户端的请求里是否包含有一个session的标识,我们称这个标识为sessionID,如果有那么就说明已经为客户端创建过session,服务器端就会按照这个sessionID将这个session检索出来(如果查找不到,就新建一个session),如果没有那么我们就新建一个session,并生成一个相关联的sessionID,并在本次响应中返回给客户端,这个session在服务器端会将它保存起来,而在客户端,我们怎样做呢,这里就用到了cookie的工作方式将这个sessionID保存起来,这样在当前用户不关闭浏览器的请况下,我们就会按照规则将这个标志传送给服务器。一般这个cookie的名字就类似于SESSIONID,这个sessionID的值是一个既不会重复,也没有固定规则的随机字符串。这里我们就发现了session和cookie的不同之处,session是和即时的会话相关联的,如果你将浏览器关闭,那么你的这个sessionID也会跟随着消失,这是为什么呢?有人说既然采用了cookie的保存方式为什么还会消失呢?因为他们的保存位置不同,在cookie的工作原理中我们看到了cookie是保存在本地磁盘的,而对于session呢,虽然我们采用了cookie的保存方式,但是并没有将他保存在本地磁盘,而是将它保存在了浏览器缓存里,当然你将浏览器关闭之后,缓存清空,那么你保存的session当然就会消失。而此时服务器上保存的sessionID则还没有删除,这就迫使服务器给sessionID设定了一个超时时间,如果超过了这个超时时间,服务器就可以认为客户端已经停止了活动,那么服务器自动删除掉这个sessionID来节约服务器的存储空间。而各个浏览器都有禁用cookie的功能,那么如果客户端禁用掉了cookie的功能后,我们应该怎么办呢?
通常会有以下三种方法:
一种URL重写的技术,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为http://...../xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一种是作为查询字符串附加在URL后面,表现形式为http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
这两种方式对于用户来说是没有区别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。
为了在整个会话的过程中始终保持状态,那么就必须在每个客户端请求服务器的路径后面都要添加上essionID。
一种是隐藏表
•Cookie 操作主要用到以下几个方法:
• response.addCookie(Cookie c):将 Cookie 写入客户端
Cookie.setMaxAge(int second):设置 Cookie 有效时间
写入Cookie的过程:
•步骤1:创建cookie对象。Cookie构造函数(名称、值),皆为字符串类型。
•步骤2:设置有效时间。sexMaxAge(),单位为s(秒)。
•步骤3:写入Http响应报文。通过response.addCookie完成。
Cookie 读取操作:
•读取客户端 Cookie方法:
• Cookie[] request.getCookies():读取客户端所有 Cookie,以数组形式返回。然后再遍历数组,根据名称找到所需的Cookie.
从客户端读取cookie:
•步骤1:调用request.getCookies( )。
•步骤2:对数组进行循环,调用每个cookie的getName方法,直到找到感兴趣的cookie位置。
二、实例代码
package gac.xdp.cookie; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class CookieDemo01 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置服务器端以UTF-8编码进行输出 response.setCharacterEncoding("UTF-8"); //设置浏览器以UTF-8编码进行接收,解决中文乱码问题 response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); //获取浏览器访问访问服务器时传递过来的cookie数组 Cookie[] cookies = request.getCookies(); //如果用户是第一次访问,那么得到的cookies将是null if (cookies!=null) { out.write("您上次访问的时间是:"); for (int i = 0; i < cookies.length; i++) { Cookie cookie = cookies[i]; if (cookie.getName().equals("lastAccessTime")) { Long lastAccessTime =Long.parseLong(cookie.getValue()); Date date = new Date(lastAccessTime); out.write(date.toLocaleString()); } } }else { out.write("这是您第一次访问本站!"); } //用户访问过之后重新设置用户的访问时间,存储到cookie中,然后发送到客户端浏览器 Cookie cookie = new Cookie("lastAccessTime", System.currentTimeMillis()+"");//创建一个cookie,cookie的名字是lastAccessTime //将cookie对象添加到response对象中,这样服务器在输出response对象中的内容时就会把cookie也输出到客户端浏览器 response.addCookie(cookie); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
效果:
在上面的例子中,在程序代码中并没有使用setMaxAge方法设置cookie的有效期,所以当关闭浏览器之后,cookie就失效了,要想在关闭了浏览器之后,cookie依然有效,那么在创建cookie时,就要为cookie设置一个有效期。如下所示:
//用户访问过之后重新设置用户的访问时间,存储到cookie中,然后发送到客户端浏览器
Cookie cookie = new Cookie("lastAccessTime", System.currentTimeMillis()+"");//创建一个cookie,cookie的名字是lastAccessTime
//设置Cookie的有效期为1天
cookie.setMaxAge(24*60*60);
//将cookie对象添加到response对象中,这样服务器在输出response对象中的内容时就会把cookie也输出到客户端浏览器
response.addCookie(cookie);
package gac.xdp.cookie; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 删除cookie */ public class CookieDemo02 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //创建一个名字为lastAccessTime的cookie Cookie cookie = new Cookie("lastAccessTime", System.currentTimeMillis()+""); //将cookie的有效期设置为0,命令浏览器删除该cookie cookie.setMaxAge(0); response.addCookie(cookie); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
session案例:
使用Session完成简单的购物车功能
•接收传过来的商品id
•使用Map集合代码购物车(key商品名称,value商品数量)
•从session中获取购物车
•如果获取不到,是第一次,创建Map,存入商品和数量
•如果获取到,不是第一次,拿到Map,判断Map中是否包含商品,如果包含取出数量+1,如果不存在,直接存入。
•把购物车存入到session中
前台:
cartlist.jsp:
<%@ 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>Insert title here</title>
</head>
<body>
<h3>手电筒<a href="/day11/cart?id=1">加入购物车</a></h3>
<h3>电视<a href="/day11/cart?id=2">加入购物车</a></h3>
<h3>冰箱<a href="/day11/cart?id=3">加入购物车</a></h3>
<h3>洗衣机<a href="/day11/cart?id=4">加入购物车</a></h3>
<h3>电话<a href="/day11/cart?id=5">加入购物车</a></h3>
<h3>电脑<a href="/day11/cart?id=6">加入购物车</a></h3>
</body>
</html>
后台程序:
Carservlet.java:
package cn.itcast.session; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * 购物车的后台 * @author Administrator * */ public class CartServlet extends HttpServlet { private static final long serialVersionUID = -4944571720622706932L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 0.Map<String,Integer> cart 车里面放商品的名称和数量。 * 1.接收参数,接收的id值。把id值转换成相应的商品。 * 2.判断是否是第一次购买(从session中获取车,如果车是null,肯定是第一次购买) * * 如果是一次购买,创建一个购物车,把该商品和数量存入到车中,再把车存入到session中。 * * 如果不是第一次购买 * * 先判断车中是否包含该商品,因为如果包含该商品了,数量+1 * * 如果不包含该商品,直接把该商品存入车中。 * 3.转发或者重定向到继续购物或者结算页面 * 4.去结算的页面(把商品的名称和数量显示到页面上) */ // 先获取请求的参数 String id = request.getParameter("id"); // 想id转换成响应的商品名称 String [] names = new String []{"手电筒","电视","冰箱","洗衣机","电话","电脑"}; // 转换成int类想 int index = Integer.parseInt(id); // 当前的商品名称 String productName = names[index - 1]; // 先获取session HttpSession session = request.getSession(); // 从session来获取车 Map<String, Integer> cart = (Map<String, Integer>)session.getAttribute("cart"); // 如果cart是null if(cart == null){ // 第一次 // 把购买的商品存入到车中 cart = new HashMap<String, Integer>(); // 把商品放入车中 cart.put(productName, 1); // 把车放入session中 session.setAttribute("cart", cart); }else{ // 先获取车中的内容,和当前购买的商品进行判断 if(cart.containsKey(productName)){ // 先获取商品的数量 Integer count = cart.get(productName); // 数量+1 count++; // 在把商品存入车中 cart.put(productName, count); // 存入到session中 session.setAttribute("cart", cart); }else{ // 直接存入到车中 cart.put(productName, 1); // 存入到session中 session.setAttribute("cart", cart); } } // 重定向到继续购物或者去结算的页面 response.sendRedirect(request.getContextPath()+"/session/gopay.jsp"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
继续购物:
gopay.jsp:
<%@ 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>Insert title here</title>
</head>
<body>
<h3><a href="/day11/session/cartlist.jsp">继续购物</a>|<a href="/day11/session/pay.jsp">去结算</a></h3>
</body>
</html>
付款:
pay.jsp:
<%@page import="java.util.Set"%> <%@page import="java.util.Map"%> <%@ 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>Insert title here</title> </head> <body> <h4>商品的信息</h4> <% // 获取车 Map<String,Integer> cart = (Map<String,Integer>)request.getSession().getAttribute("cart"); // 购物车不为空 if(cart != null){ // 获取车的商品名称和数量 Set<String> names = cart.keySet(); // 循环set集合,商品的名称 for(String name : names){ %> <h3>亲,您购买了<%= name %>,数量是<%= cart.get(name) %></h3> <% } }else{ %> <h3>亲,您还没有<a href="/day11/session/cartlist.jsp">败家</a>,请您快去败家</h3> <% } %> </body> </html>
实验演示禁用Cookie后servlet共享数据导致的问题。
解决方案:URL重写
•response. encodeRedirectURL(java.lang.String url)
•用于对sendRedirect方法后的url地址进行重写。
•response. encodeURL(java.lang.String url)
•用于对表单action和超链接的url地址进行重写
推荐几篇参考博客javaweb学习总结(十一)——使用Cookie进行会话管理