Web-第十天 Cookie&Session学习 今日内容介绍 案例一:记录用户的上次访问时间 案例二:一次性验证码 今日内容学习目标 可以响应给浏览器Cookie信息 可以接收浏览器携带的cookie信息 理解cookie的执行原理 可以对session作用域的数据进行操作:存放、获得、移除 理解session的执行原理 当用户访问某些Web应用时,经常会显示出该用户上一次的访问时间。例如,QQ登录成功后,会显示用户上次的登录时间。通过本任务,读者将学会如何使用Cookie技术实现显示用户上次的访问时间的功能。 在日常生活中,从拨通电话到挂断电话之间的一连串的你问我答的过程就是一个会话。Web应用中的会话过程类似于生活中的打电话过程,它指的是一个客户端(浏览器)与Web服务器之间连续发生的一系列请求和响应过程,例如,一个用户在某网站上的整个购物过程就是一个会话。 在打电话过程中,通话双方会有通话内容,同样,在客户端与服务器端交互的过程中,也会产生一些数据。例如,用户甲和乙分别登录了购物网站,甲购买了一个Nokia手机,乙购买了一个Ipad,当这两个用户结账时,Web服务器需要对用户甲和乙的信息分别进行保存。在前面章节讲解的对象中,HttpServletRequest对象和ServletContext对象都可以对数据进行保存,但是这两个对象都不可行,具体原因如下: (1)客户端请求Web服务器时,针对每次HTTP请求,Web服务器都会创建一个HttpServletRequest对象,该对象只能保存本次请求所传递的数据。由于购买和结账是两个不同的请求,因此,在发送结账请求时,之前购买请求中的数据将会丢失。 (2)使用ServletContext对象保存数据时,由于同一个Web应用共享的是同一个ServletContext对象,因此,当用户在发送结账请求时,由于无法区分哪些商品是哪个用户所购买的,而会将该购物网站中所有用户购买的商品进行结算,这显然也是不可行的。 (3)为了保存会话过程中产生的数据,在Servlet技术中,提供了两个用于保存会话数据的对象,分别是Cookie和Session。关于Cookie和Session的相关知识,将在下面的小节进行详细讲解。 Cookie 和浏览器缓存有什么区别? 共同点:浏览器缓存可以缓存任意内容(上网浏览的所有内容)、cookie只是服务器需要浏览器缓存数据(浏览器缓存中一部分) 在现实生活中,当顾客在购物时,商城经常会赠送顾客一张会员卡,卡上记录用户的个人信息(姓名,手机号等)、消费额度和积分额度等。顾客一旦接受了会员卡,以后每次光临该商场时,都可以使用这张会员卡,商场也将根据会员卡上的消费记录计算会员的优惠额度和累加积分。 在Web应用中,Cookie的功能类似于这张会员卡,当用户通过浏览器访问Web服务器时,服务器会给客户端发送一些信息,这些信息都保存在Cookie中。这样,当该浏览器再次访问服务器时,都会在请求头中将Cookie发送给服务器,方便服务器对浏览器做出正确的响应。 服务器向客户端发送Cookie时,会在HTTP响应头字段中增加Set-Cookie响应头字段。Set-Cookie头字段中设置的Cookie遵循一定的语法格式,具体示例如下: Set-Cookie: user=javahelp; Path=/; 在上述示例中,user表示Cookie的名称,javahelp表示Cookie的值,Path表示Cookie的属性。需要注意的是,Cookie必须以键值对的形式存在,其属性可以有多个,但这些属性之间必须用分号;和空格分隔。 了解了Cookie信息的发送方式后,接下来,通过一张图来描述Cookie在浏览器和服务器之间的传输过程,具体如图5-1所示。 图5-1Cookie在浏览器和服务器之间传输的过程 在图5-1中,描述了Cookie在浏览器和服务器之间的传输过程。当用户第一次访问服务器时,服务器会在响应消息中增加Set-Cookie头字段,将用户信息以Cookie的形式发送给浏览器。一旦用户浏览器接受了服务器发送的Cookie信息,就会将它保存在浏览器的缓冲区中,这样,当浏览器后续访问该服务器时,都会在请求消息中将用户信息以Cookie的形式发送给Web服务器,从而使服务器端分辨出当前请求是由哪个用户发出的。 Cookie将用户的信息保存到客户端浏览器的一个技术,当下次访问的时候,浏览器会自动携带Cookie的信息过来到服务器端. Cookie的基本使用: 创建cookie : new Cookie(name,value) , javax.servlet.http.Cookie 将cookie发送给浏览器: HttpServletResponse.addCookie(javax.servlet.http.Cookie) 接收浏览器携带的所有cookie: HttpServletRequest.getCookies() 第一步.获得从客户端带过来的所有的Cookie: 第二步.从所有的Cookie中查找指定名称的Cookie: 第三步.判断是否是第一次访问: * 是第一次:显示欢迎 * 不是第一次:显示欢迎 同时显示上次访问时间. 第四步.记录当前的时间,并且利用Cookie将时间回写到浏览器端. public class VisitServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 1.获得从客户端带过来的所有的Cookie: * 2.从所有的Cookie中查找指定名称的Cookie: * 3.判断是否是第一次访问: * * 是第一次:显示欢迎 * * 不是第一次:显示欢迎 同时显示上次访问时间. * 4.记录当前的时间,并且利用Cookie将时间回写到浏览器端. */ // 获得客户端的所有的Cookie: Cookie[] cookies = request.getCookies(); // 从cookies的数组中查找指定名称的Cookie: Cookie cookie = CookieUtils.findCookie(cookies, "visitTime"); // 判断是否是第一次访问: response.setContentType("text/html;charset=UTF-8"); if(cookie == null){ // 第一次访问 response.getWriter().println("<h1>欢迎来到本网站!</h1>"); }else{ // 不是第一次 long time = Long.parseLong(cookie.getValue()); Date date = new Date(time); response.getWriter().println("<h1>欢迎来到本网站!您的上次访问时间是:"+date.toLocaleString()+"</h1>"); } // 记录当前的时间,回写到Cookie中. Cookie c = new Cookie("visitTime",""+System.currentTimeMillis()); response.addCookie(c); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } 会话级别的Cookie:默认的.关闭了浏览器Cookie就销毁了. 持久级别的Cookie:需要设置有效时长的.关闭浏览器也不会销毁的Cookie. setMaxAge(int expiry); 以秒为单位的时间,超过了该时间后Cookie会自动销毁. setMaxAge(0),手动删除持久性的Cookie。(前提:path和name必须一致) setPath(String uri); 设置Cookie的有效路径. 例如: 1)cookie.setPath("/day16/demo"); 表示day16 项目下,【demo目录】下所有的servlet, 都可以访问当前cookie。但“/day16”或“/day16/aaa” 将不能访问。 2)cookie.setPath("/day16"); 表示【day16 项目】下的所有servlet都可以访问当前cookie 3)cookie.setPath("/"); 表示【tomcat下】的所有的web项目,都可以访问当前cookie cookie唯一表示: 唯一标示:domain + path + name (类似Java中 包 + 类名) domain 域名,不同的网站使用的是不同的域名,cookie就不同。 path 路径,通过cookie.setPath(…)设置的内容。 name cookie名称,通过 new Cookie(name , …) 确定的内容。 例如:以下表示的是两个cookie,可以同时存在。 /web/a/b/cookieName /web/a/cookieName 如果路径和名称一样,两次addCookie(),后者将覆盖前者。 方法名 描述 getName() 获得cookie名称。 getValue() 获得cookie的值。 setMaxAge(int expiry) 设置cookie的有效时间。 如果没有设置,cookie只缓存浏览器缓存中,浏览器关闭,cookie删除。 如果设置有效时间,在时间范围内,cookie被写入到浏览器端,关闭浏览器下次访问仍可获得,直到过期。 setPath(java.lang.String uri) 设置cookie允许被访问的路径。设置的路径,以及子路径都被允许访问。 例如:setPath("/web/a/b"); http://localhost:8080/web/a/b/oneServlet,可访问(当前路径) http://localhost:8080/web/a/b/c/oneServlet,可访问(子路径) http://localhost:8080/web/a/c/oneServlet,不允许访问(无关路径) 常见设置:setPath(“/”) ,当前tomcat下的所有的web项目都可以访问 验证码的作用: 防止恶意提交表单 当人们去医院就诊时,就诊病人需要办理医院的就诊卡,该卡上只有卡号,而没有其它信息。但病人每次去该医院就诊时,只要出示就诊卡,医务人员便可根据卡号查询到病人的就诊信息。 Session技术就好比医院发放给病人的就医卡和医院为每个病人保留病例档案的过程。当浏览器访问Web服务器时,Servlet容器就会创建一个Session对象和ID属性,其中,Session对象就相当于病历档案,ID就相当于就诊卡号。当客户端后续访问服务器时,只要将标识号传递给服务器,服务器就能判断出该请求是哪个客户端发送的,从而选择与之对应的Session对象为其服务。 需要注意的是,由于客户端需要接收、记录和回送Session对象的ID,因此,通常情况下,Session是借助Cookie技术来传递ID属性的。 为了使读者更好的理解Session,接下来,以网站购物为例,通过一张图来描述Session保存用户信息的原理,具体如图5-5所示。 图1-1 Session保存用户信息的过程 在图5-5中,用户甲和乙都调用buyServlet将商品添加到购物车,调用payServlet进行商品结算。由于甲和乙购买商品的过程类似,在此,以用户甲为例进行详细说明。当用户甲访问购物网站时,服务器为甲创建了一个Session对象(相当于购物车)。 当甲将Nokia手机添加到购物车时,Nokia手机的信息便存放到了Session对象中。 同时,服务器将Session对象的ID属性以Cookie (Set-Cookie: JSESSIONID=111)的形式返回给甲的浏览器。当甲完成购物进行结账时,需要向服务器发送结账请求,这时,浏览器自动在请求消息头中将Cookie (Cookie: JSESSIONID=111)信息回送给服务器,服务器根据ID属性找到为用户甲所创建的Session对象,并将Session对象中所存放的Nokia手机信息取出进行结算。 使用servlet生成验证码时,我们需要在服务器记录一份生成的随机字符,当用户提交填写的数据时,将用户输入的数据和服务器缓存的数据进行比对。 将随机字符串保存在ServletContext中或者request中是否可以? 将数据存放到ServletContext,多个用户共享一个验证码。 将数据存放到request作用域,多次请求不能共享数据。 我们发现将数据保存到ServletContext和request中是存在问题的,那么就需要使用会话技术保存用户的私有信息. * Cookie是有大小和个数的限制的.Session存到服务器端的技术,没有大小和个数的限制. * Cookie相对于Session来讲不安全. Session是与每个请求消息紧密相关的,为此,HttpServletRequest定义了用于获取Session对象的getSession()方法,该方法有两种重载形式,具体如下。 public HttpSession getSession(boolean create) public HttpSession getSession() 上面重载的两个方法都用于返回与当前请求相关的HttpSession对象。不同的是,第一个getSession()方法根据传递的参数来判断是否创建新的HttpSession对象,如果参数为true,则在相关的HttpSession对象不存在时创建并返回新的HttpSession对象,否则不创建新的HttpSession对象,而是返回null。第二个getSession()方法则相当于第一个方法参数为true时的情况,在相关的HttpSession对象不存在时总是创建新的HttpSession对象。 要想使用HttpSession对象管理会话数据,不仅需要获取到HttpSession对象,还需要了解HttpSession对象的相关方法。HttpSession接口中定义的操作会话数据的常用方法如表5-2所示。 表5-1 HttpSession接口中的常用方法 方法声明 功能描述 String getId() 用于返回与当前HttpSession对象关联的会话标识号 long getCreationTime() 返回Session创建的时间,这个时间是创建Session的时间与1970年1月1日00:00:00之间时间差的毫秒表示形式 long getLastAccessedTime() 返回客户端最后一次发送与Session相关请求的时间,这个时间是发送请求的时间与1970年1月1日00:00:00之间时间差的毫秒表示形式 void setMaxInactiveInterval(int interval) 用于设置当前HttpSession对象可空闲的以秒为单位的最长时间,也就是修改当前会话的默认超时间隔 boolean isNew() 判断当前HttpSession对象是否是新创建的 void invalidate() 用于强制使Session对象无效 ServletContext getServletContext() 用于返回当前HttpSession对象所属于的WEB应用程序对象,即代表当前WEB应用程序的ServletContext对象 void setAttribite(String name,Object value) 用于将一个对象与一个名称关联后存储到当前的HttpSession对象中 String getAttribute() 用于从当前HttpSession对象中返回指定名称的属性对象 void removeAttribute(String name) 用于从当前HttpSession对象中删除指定名称的属性 表5-2列举了HttpSession接口中的常用方法,这些方法都是用来操作HttpSession对象的。 将随机生成的字母或数字存入到Session中: 验证码的校验: public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 进行验证码校验: String code1 = request.getParameter("checkcode"); String code2 = (String)request.getSession().getAttribute("checkcode"); request.getSession().removeAttribute("checkcode"); if(!code1.equalsIgnoreCase(code2)){ // 验证码有错误: request.setAttribute("msg", "验证码输入错误!"); request.getRequestDispatcher("/demo4-checkcode/login.jsp").forward(request, response); return; } // 登录代码: } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } ServletContext:针对一个WEB应用。一个WEB应用只有一个ServletContext对象,使用该对象保存的数据在整个WEB应用中都有效。 创建:服务器启动的时候. 销毁:服务器关闭的时候或者项目移除的时候. HttpSession:针对一次会话。使用该对象保存数据,一次会话(多次请求)内数据有效。 创建:服务器第一次调用getSession()的时候.服务器创建session的对象. 销毁: 1. 非正常关闭服务器(正常关闭:Session被序列化) 2. Session过期了,默认时间是30分钟. 3. 手动调用session的invalidate的方法. HttpServletRequest :针对一次请求。使用该对象保存数据,一次请求(一个页面,如果是请求转发多个页面)内数据有效. 创建:客户端向服务器发送一次请求 销毁:服务器为这次请求作出响应之后,销毁request. 三个作用域对象操作的API相同 存放数据:setAttribute(name,value) 获得数据:getAttribute(name) 删除数据:removeAttribute(name) /** * 1.接收参数ID * 2.获得从浏览器带过来的Cookie的信息. * 3.判断是否是第一次浏览商品: * * 如果是第一次: * * 创建一个Cookie将商品ID回写到浏览器. * * 如果不是第一次: * * 获得Cookie中的值(所有浏览过商品的ID) 1-3 * * 判断现在浏览的商品是否已经在浏览器记录中. * * 已经存在:1-3 点击3 * * 删除当前元素,将该元素添加到最前面. * * 不存在: 1-3 点击 4 1-3-2 点击4 * * 判断长度: * * 没有超出界限:直接添加到最前面. * * 如果到达界限:删除最后面一个元素,将点击的这个添加到最前面. * * 创建一个Cookie将商品的ID们回写到浏览器. * 4.页面跳转到商品详情的页面. */ public class HistoryServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 1.接收参数ID * 2.获得从浏览器带过来的Cookie的信息. * 3.判断是否是第一次浏览商品: * * 如果是第一次: * * 创建一个Cookie将商品ID回写到浏览器. * * 如果不是第一次: * * 获得Cookie中的值(所有浏览过商品的ID) 1-3 * * 判断现在浏览的商品是否已经在浏览器记录中. * * 已经存在:1-3 点击3 * * 删除当前元素,将该元素添加到最前面. * * 不存在: 1-3 点击 4 1-3-2 点击4 * * 判断长度: * * 没有超出界限:直接添加到最前面. * * 如果到达界限:删除最后面一个元素,将点击的这个添加到最前面. * * 创建一个Cookie将商品的ID们回写到浏览器. * 4.页面跳转到商品详情的页面. */ // 1.接收参数ID: String id = request.getParameter("id"); // 2.获得从客户端带过来的所有的Cookie: Cookie[] cookies = request.getCookies(); // 3.查找指定名称Cookie, Cookie cookie = CookieUtils.findCookie(cookies, "history"); // 4.判断是否是第一次浏览商品: if(cookie == null){ // 第一次浏览商品: Cookie c = new Cookie("history",id); // 设置有效路径: c.setPath("/day11"); // 设置有效时长: c.setMaxAge(7*24*60*60); // 将Cookie回写到浏览器: response.addCookie(c); }else{ // 不是第一次访问: 1-3 // 判断浏览的商品是否已经在浏览记录中: String value = cookie.getValue(); // 1-3 // 切分: String[] ids = value.split("-"); // [1,3] // 创建LinkedList:进行增删改的时候比较方便. LinkedList<String> list = new LinkedList<String>(Arrays.asList(ids)); // 判断是否商品已经在浏览记录中: if(list.contains(id)){ // 说明商品已经在浏览记录中. 1-3 点击 3 --》 3-1 list.remove(id); list.addFirst(id); }else{ // 商品没有在浏览记录中 if(list.size()>=3){ // 超过长度 1-3-2 点击 4 --> 4-1-3 list.removeLast(); list.addFirst(id); }else{ // 没有超过 1-3 点击 4--》 4-1-3 list.addFirst(id); } } // 使用-连接集合中的数据: StringBuffer sb = new StringBuffer(); for (String string : list) { sb.append(string).append("-"); } String history = sb.toString().substring(0, sb.toString().length()-1); Cookie c = new Cookie("history",history); // 设置有效路径: c.setPath("/day11"); // 设置有效时长: c.setMaxAge(7*24*60*60); // 将Cookie回写到浏览器: response.addCookie(c); } // 页面的跳转: response.sendRedirect(request.getContextPath()+"/demo2-history/product_info"+id+".htm"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } public class ClearHistoryServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 删除持久性的Cookie: Cookie cookie = new Cookie("history",""); cookie.setPath("/day11"); cookie.setMaxAge(0); response.addCookie(cookie); response.sendRedirect(request.getContextPath()+"/demo2-history/product_list.jsp"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } |
|