Advertisement

会话跟踪技术:HttpSession和Cookie

阅读量:

一、会话跟踪技术

1 什么是会话跟踪技术

为了更好地理解这一概念,请先掌握会话的基本含义。我们可以将之理解为客户端与服务器之间的一次互动过程,在整个过程中可能会有多次交互操作。例如,在拨打10086电话时,请将自己视为客户端,在此对话中扮演相应的角色至关重要。一旦双方接通电话,则对话便正式开始;当一方挂断连接表示对话结束时,则标志着整个交流活动的终结。在通话期间会产生多个请求信息以完成各项操作需求。

当一个客户端在JavaWeb环境中首次向服务器提交请求时,在发送该请求后 session便建立起来了; session一直持续到客户端关闭浏览器为止。


在一个会话的多个请求中共享数据,这就是会话跟踪技术。

例如在一个会话中的请求如下:

l 请求银行主页;

l 请求登录(请求参数是用户名和密码);

l 请求转账(请求参数与转账相关的数据);

l 请求信誉卡还款(请求参数与还款相关的数据)。

在此对话中应确保相关信息在此对话内被分享。由于登录的是张三,在这些操作都是针对张三进行的转账与还款。这表明我们在一个对话期间内应具备分享数据的能力。

2 会话路径技术使用Cookie或session完成

我们了解HTTP协议属于无状态传输机制,在此框架下每个网络请求都是独立事件!单独请求无法保存前一次操作的状态信息。然而,在HTTP协议体系中设置了机制允许利用Cookie变量实现会话持续性功能!

在JavaWeb应用开发中,采用Session组件来实现会话管理功能,并且其底层依赖于基于Cookie的技术。

二、Cookie

1 Cookie概述

1.1 什么叫Cookie

在中文中“Cookie”对应的词意包括“小甜点”以及“小饼干”。从技术角度来看,在HTTP协议中这种数据项被定义为一种信息载体,在技术实现上通常由一个键值对构成。实际上,在技术实现上,“Cookie”通常由一个键值对构成:当客户从服务端获取响应内容后,在后续访问中客户会自动携带这种数据项返回至服务提供方。

Cookies由服务器生成,并通过响应传递给客户端。客户机会将这些Cookies存储本地,并记录下这些Cookies的来源信息。在客户发送请求时... 该服务相关的Cookies会被包含在请求中传输至服务器处理!

1.2 Cookie规范

l Cookie大小上限为4KB;

l 一个服务器最多在客户端浏览器上保存20个Cookie;

l 一个浏览器最多保存300个Cookie;

这一数据仅限于HTTP Cookie规范,在当前环境下,在浏览器之间的竞争日益激烈的情况下

请记住各不同的浏览器间不会共用Cookie。具体来说,在你在使用IE浏览器访问服务器时,请注意 server会将 cookie发送出去并被 IE接收并进而保存下来;而如果你切换到 Firefox 浏览器再进行 server连接操作,则无法将 IE接收并保存下来的 cookie传输到 server那边。

1.3 Cookie与HTTP头

Cookie是通过HTTP请求和响应头在客户端和服务器端传递的:

l Cookie:请求头 ,客户端发送给服务器端;

格式:Cookie: a=A; b=B; c=C。即多个Cookie用分号离开;

l Set-Cookie:响应头 ,服务器端发送给客户端;

一个Cookie对应一个Set-Cookie:

Set-Cookie: a=A

Set-Cookie: b=B

Set-Cookie: c=C

1.4 Cookie的覆盖

如果服务器端反复发送相同的 Cookie,则会替代原有的 Cookie。例如,在第一个请求中, 服务器端传递了一个 Set-Cookie 指令: a=A;而在第二个请求中, 服务器端则传递了另一个 Set-Cookie 指令: a=AA;这样, 在客户端只保留最后一个设置的有效 Cookie(即 a=AA)。

1.5 Cookie第一例

在这个案例中:
当客户端向AServlet发送请求时,
AServlet在响应过程中生成Cookie对象,
服务器端系统会自动将这些 Cookie 对象保存到本地文件系统中。
接着,在另一个请求中,
服务器端系统会在下一个 HTTP 头信息中设置这些 cookie 参数。
最后,
BServlet提取并处理这些 cookie 对象以完成任务。

复制代码
 package cn.itcast.servlet;

    
  
    
 import java.io.IOException;
    
 import java.util.UUID;
    
  
    
 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
    
  * @author Administrator
    
  * */
    
 public class AServlet extends HttpServlet {
    
 	public void doGet(HttpServletRequest request, HttpServletResponse response)
    
 			throws ServletException, IOException {
    
 		response.setContentType("text/html;charset=utf-8");
    
 		
    
 		String id = UUID.randomUUID().toString();//生成一个随机字符串
    
 		Cookie cookie = new Cookie("id", id);//创建Cookie对象,指定名字和值
    
 		response.addCookie(cookie);//在响应中添加Cookie对象
    
 		response.getWriter().print("已经给你发送了ID");
    
 	}
    
 }
复制代码
 package cn.itcast.servlet;

    
  
    
 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
    
  * @author Administrator
    
  * */
    
 public class BServlet extends HttpServlet {
    
 	public void doGet(HttpServletRequest request, HttpServletResponse response)
    
 			throws ServletException, IOException {
    
 		response.setContentType("text/html;charset=utf-8");
    
 		
    
 		Cookie[] cs = request.getCookies();//获取请求中的Cookie
    
 		if(cs != null) {//如果请求中存在Cookie
    
 			for(Cookie c : cs) {//遍历所有Cookie
    
 				if(c.getName().equals("id")) {//获取Cookie名字,如果Cookie名字是id
    
 					response.getWriter().print("您的ID是:" + c.getValue());//打印Cookie值
    
 				}
    
 			}
    
 		}
    
 	}
    
 }

2 Cookie的生命

2.1 什么是Cookie的生命

Cookie不仅仅包含name和value, 而且也是存在意义的。所谓生命即为Cookie在客户端的生存期, 通过设置maxAge(int)属性即可实现设置其有效时间。

cookie.setMaxAge(-1):该属性的默认值为-1。该属性表示该cookie仅存在于浏览器内存中。当浏览器窗口关闭时, 该cookie将被删除.

该字段设置为60分钟用于设置Cookie的有效期,在有效期内的情况下,在浏览器关闭或客户端重启后仍会被保存

l cookie.setMaxAge(0): 设置为零是一个特殊标记值,并且意味着该cookie将被清除。这表示若系统中已存在该Cookie实例,则可以通过调用setMaxAge(0)将其清除。无论是存储在浏览器内存还是客户端存储介质中。

2.3 案例:显示上次访问时间

l 创建Cookie,名为lasttime,值为当前时间,添加到response中;

l 在AServlet中获取请求中名为lasttime的Cookie;

l 如果系统检测到您首次访问此站点,则会返回提示信息'您是首次访问此站点';否则会将【

AServlet.java:

复制代码
 public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

    
 		response.setContentType("text/html;charset=utf-8");
    
 		
    
 		Cookie cookie = new Cookie("lasttime", new Date().toString()); 
    
 		cookie.setMaxAge(60 * 60); 
    
 		response.addCookie(cookie); 
    
 		
    
 		Cookie[] cs = request.getCookies(); 
    
 		String s = "您是首次访问本站!";
    
 		if(cs != null) { 
    
 			for(Cookie c : cs) { 
    
 				if(c.getName().equals("lasttime")) { 
    
 					s = "您上次的访问时间是:" + c.getValue(); 
    
 				}
    
 			}
    
 		}
    
 		
    
 		response.getWriter().print(s); 
    
 	}

3 Cookie的path

3.1 什么是Cookie的路径

目前的应用系统A向客户端发送了十个Cookie。这表明无论访问哪一个Servlet都会将这些十个Cookie包含在相应的请求中。然而可能存在的情况是仅有一个ServletContext会解析请求中的Cookies而其他的ServletContext则不会主动获取这些Cookies因此从某种程度上说浏览器有时会多发一些Cookies显得有些多余

浏览器被用来配置 Cookie 路径以确定它们会被访问时包含什么类型的 Cookie。

3.2 Cookie路径与请求路径的关系

下面我们来看看Cookie路径的作用:

下面是客户端浏览器保存的3个Cookie的路径:

a: /cookietest;

b: /cookietest/servlet;

c: /cookietest/jsp;

下面是浏览器请求的URL:

A: http://localhost:8080/cookietest/AServlet;

B: http://localhost:8080/cookietest/servlet/BServlet;

C: http://localhost:8080/cookietest/jsp/CServlet;

l 请求A时,会在请求中包含a;

l 请求B时,会在请求中包含a、b;

l 请求C时,会在请求中包含a、c;

换句话说,在请求路径中包含了Cookie路径的情况下,则该请求将包含此Cookie;否则不包含

l A请求的URL包含了‘/cookietest’会被包含在请求中,并且路径为‘/cookietest’的Cookie会被包含。

因此,在该HTTP请求中不仅包含了URL路径'/'cookietest'''这一项参数数据外,并且还包含有'/'cookietest/servlet'''这一嵌套的子路径参数信息;

l B请求的URL包含以下内容:' CookiTest' 以及 ' CookiTestjsp' ,因此该请求的路径上包含了这两个Cookie

3.3 设置Cookie的路径

设置Cookie的路径需要使用setPath()方法,例如:

cookie.setPath(“/cookietest/servlet”);

注意,并非是将cookie保存在客户端存储的位置信息(即保存路径),而是仅为了设置Cookie属性中的path字段

例如,在某些情况下,默认情况下会继承父请求的位置信息

当访问http://localhost:8080/cookietest/AServlet时,在该AServlet上设置Cookie,默认使用/cookietest作为路径;

在访问该URL地址时设置Cookie;其默认路径设置为/cookietest/servlet;

在访问http://localhost:8080/cookietest/jsp/BServlet时设置了Cookie的默认路径为/cookietest/jsp。

4 Cookie中保存中文

Cookie中的name和value字段均不得包含中文字符;若需在Cookie中显示中文内容,则必须先将该文字进行URL编码处理。

然后把编码后的字符串放到Cookie中。

向客户端响应中添加Cookie:

复制代码
  String name = URLEncoder.encode("姓名", "UTF-8");//使用URL编码

    
 	String value = URLEncoder.encode("张三", "UTF-8");//使用URL编码
    
  
    
  	Cookie c = new Cookie(name, value); 
    
 	c.setMaxAge(3600);
    
 	response.addCookie(c);

从客户端请求中获取Cookie:

复制代码
  response.setContentType("text/html;charset=utf-8");

    
 		Cookie[] cs = request.getCookies();
    
 		if(cs != null) {
    
 			for(Cookie c : cs) {
    
 				String name = URLDecoder.decode(c.getName(), "UTF-8");//使用URL解码
    
 				String value = URLDecoder.decode(c.getValue(), "UTF-8");//使用URL解码
    
  				String s = name + ": " + value + "<br/>";
    
 				response.getWriter().print(s);
    
 			}
    
 		}

5 案例:显示曾经浏览过的商品

index.jsp

复制代码
 <body>

    
     <h1>商品列表</h1>
    
     <a href="/day06_3/GoodServlet?name=ThinkPad">ThinkPad</a><br/>
    
     <a href="/day06_3/GoodServlet?name=Lenovo">Lenovo</a><br/>
    
     <a href="/day06_3/GoodServlet?name=Apple">Apple</a><br/>
    
     <a href="/day06_3/GoodServlet?name=HP">HP</a><br/>
    
     <a href="/day06_3/GoodServlet?name=SONY">SONY</a><br/>
    
     <a href="/day06_3/GoodServlet?name=ACER">ACER</a><br/>
    
     <a href="/day06_3/GoodServlet?name=DELL">DELL</a><br/>
    
     
    
     <hr/>
    
     您浏览过的商品:
    
     <%
    
     	Cookie[] cs = request.getCookies();
    
     	if(cs != null) {
    
     		for(Cookie c : cs) {
    
     			if(c.getName().equals("goods")) {
    
     				out.print(c.getValue());
    
     			}
    
     		}
    
     	}
    
     %>
    
   </body>

GoodServlet

复制代码
 public class GoodServlet extends HttpServlet {

    
 	public void doGet(HttpServletRequest request, HttpServletResponse response)
    
 			throws ServletException, IOException {
    
 		String goodName = request.getParameter("name");
    
 		String goods = CookieUtils.getCookValue(request, "goods");
    
 		
    
 		if(goods != null) {
    
 			String[] arr = goods.split(", ");
    
 			Set<String> goodSet = new LinkedHashSet(Arrays.asList(arr));
    
 			goodSet.add(goodName);
    
 			goods = goodSet.toString();
    
 			goods = goods.substring(1, goods.length() - 1);
    
 		} else {
    
 			goods = goodName;
    
 		}
    
 		Cookie cookie = new Cookie("goods", goods);
    
 		cookie.setMaxAge(1 * 60 * 60 * 24);
    
 		response.addCookie(cookie);
    
 		
    
 		response.sendRedirect("/day06_3/index.jsp");
    
 	}
    
 }

CookieUtils

复制代码
 public class CookieUtils {

    
 	public static String getCookValue(HttpServletRequest request, String name) {
    
 		Cookie[] cs = request.getCookies();
    
 		if(cs == null) {
    
 			return null;
    
 		}
    
 		for(Cookie c : cs) {
    
 			if(c.getName().equals(name)) {
    
 				return c.getValue();
    
 			}
    
 		}
    
 		return null;
    
 	}
    
 }

三、HttpSession

1 HttpSession概述

1.1 什么是HttpSesssion

该HTTSession接口可以表示一个会话。我们可以通过将一个会话内的共享信息保存到HttpSession对象中来实现数据存储。

1.2 获取HttpSession对象

l HttpSession request.getSession(): 如果当前会话已存在 session 对象,则立即返回;否则将创建一个新 session 并将其返回;

在HttpSession对象中定义了一个名为getSession的布尔型方法,在调用该方法时需传递一个布尔型参数:若指定true值,则其行为与普通getSession方法一致;若指定false值,则会话存在时将返回当前 getSession对象;否则将返回null。

1.3 HttpSession是域对象

之前已经掌握了HttpServletRequest和ServletContext这两种技术,并且也了解到HttpSession这一概念。这些属于Servlet组件中的核心组件类型。在Servlet组件中有三种这样的核心组件类型,在JSP开发环境中还可以多出一种可用的核心组件类型。明天我们将进一步讲解JSP的第四个核心组件类型。

HttpServletResponse:当一个请求数被发起时会创建一个Request实例,在同一时间同一个Request实例可以在多个客户端使用(例如当一个请求数从AServlet转发到BServlet时),因此AServlet和BServlet能够共享同一份Request实例中的数据。

ServletContext:每个Web应用程序只能创建唯一的一个ServletContext对象,在该对象的有效期内(即服务器未重新启动之前),它所管理的数据可以在整个Web应用环境中实现共享;

HTTP容器:创建HTTP容器需要初始化一个容器实例;每个容器实例内部的线程之间能够共享资源以提高并发处理能力

下面是session的域方法:

l void setAttribute(String name, Object value):用于将对象保存为属性值,并也可被视为设置域属性值。例如,在session中调用setAttribute方法时,会将字符串"xxx"作为键名,并将字符串"XXX"作为其对应的值进行存储。请注意,在连续调用该方法时使用相同的名称参数会导致覆盖之前的结果;这种行为与Map数据结构的行为一致。

Class getAttribute(String name): 用于从session中读取数据,在读取前需先将数据存储起来;例如: String value = (String) session.getAttribute("xxx"); 获取名为'xxx'的属性域;

public void removeAttribute(String name):该方法负责删除HttpSession中的指定域属性;当指定的domain attribute不存在时,则会忽略该参数。

l Enumeration getAttributeNames():获取所有域属性的名称;

2 登录案例

需要的页面:

l login.jsp:登录页面,提供登录表单;

l index1.jsp:主页,显示当前用户名称,如果没有登录,显示您还没登录;

l index2.jsp:主页,显示当前用户名称,如果没有登录,显示您还没登录;

Servlet:

l LoginServlet:当从login.jsp页面提交表单时,请问您是否正在请求本ServletContext?在本ServletContext中接收并解析用户提交的 username 和 password 后进行验证操作。若发现用户输入的 username 或 password 存在错误,则会向客户端发送提示信息'用户名或密码无效';若验证结果为成功,则将用户的 username 存储于 Session 对象中,并请客户端跳转至 index1.jsp 页面继续下一步操作流程。

若未登录者访问 index1.jsp 或 index2.jsp,则将提示“未 login 提示”。一旦在 login.jsp 中完成 login,在 index1.jsp 页面将显示出当前 username。同样地,在 index2.jsp 访问无需再次 login 即可获取到 username。这是因为由于 session 对象在同一会话期间保持一致,在此期间 index1.jsp 和 index2.jsp 都会从 session 中读取 username 资料。

login.jsp

复制代码
 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

    
  
    
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    
 <html>
    
   <head>
    
     <title>login.jsp</title>
    
   </head>
    
   
    
   <body>
    
     <h1>login.jsp</h1>
    
     <hr/>
    
     <form action="/day06_4/LoginServlet" method="post">
    
     	用户名:<input type="text" name="username" /><br/>
    
     <input type="submit" value="Submit"/>
    
     </form>
    
   </body>
    
 </html>

index1.jsp

复制代码
 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

    
  
    
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    
 <html>
    
   <head>
    
     <title>index1.jsp</title>
    
   </head>
    
   
    
   <body>
    
 <h1>index1.jsp</h1>
    
 <%
    
 	String username = (String)session.getAttribute("username"); 
    
 	if(username == null) {
    
 		out.print("您还没有登录!"); 
    
 	} else {
    
 		out.print("用户名:" + username); 
    
 	}
    
 %>
    
 <hr/>
    
 <a href="/day06_4/index2.jsp">index2</a>
    
   </body>
    
 </html>

index2.jsp

复制代码
 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

    
  
    
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    
 <html>
    
   <head>
    
     <title>index2.jsp</title>
    
   </head>
    
   
    
   <body>
    
 <h1>index2.jsp</h1>
    
 <%
    
 	String username = (String)session.getAttribute("username");
    
 	if(username == null) {
    
 		out.print("您还没有登录!");
    
 	} else {
    
 		out.print("用户名:" + username);
    
 	}
    
 %>
    
 <hr/>
    
 <a href="/day06_4/index1.jsp">index1</a>
    
   </body>
    
 </html>

LoginServlet

复制代码
 public class LoginServlet extends HttpServlet {

    
 	public void doPost(HttpServletRequest request, HttpServletResponse response)
    
 			throws ServletException, IOException {
    
 		request.setCharacterEncoding("utf-8");
    
 		response.setContentType("text/html;charset=utf-8");
    
 		
    
 		String username = request.getParameter("username"); 
    
 		
    
 		if(username.equalsIgnoreCase("itcast") ) {
    
 			response.getWriter().print("用户名或密码错误!");
    
 		} else {
    
 			HttpSession session = request.getSession(); 
    
 			session.setAttribute("username", username); 
    
 			response.sendRedirect("/day06_4/index1.jsp"); 
    
 		}
    
 	}
    
 }

3 session的实现原理

session底层是依赖Cookie的!我们来理解一下session的原理吧!

当我初次访问银行时, 由于缺乏账户信息, 因此必须开设一个新的账户. 最终我领到了一张信用卡, 系统记录了我的账户信息, 这些资金被存储在银行指定的账户里, 而携带的信息卡片包含了所有必要的个人信息.

下一次访问银行时,我只需要携带我的银行卡,并不需要重新开通个人账户.只要携带我的银行卡,这样在使用银行卡进行的操作中涉及的就是我本人的账户信息.

当第一次调用 session 时,在线服务系统会自动发起请求以创建该 created session。为了管理会话流程, 服务器必须先创建一个 session, 将其存储在本地内存中, 并通过 cookie 标识符 sessionId 将其发送给客户端。随后, 客户端将收到这个 identifier, 并且在这个过程中还会接收相关联的数据信息。

在客户端进行再次访问时,在请求中会携带上一个会话标识符(sessionId),从而利用该会话标识符定位到相应的会话信息。因此,在这种情况下不需要生成新的会话标识符就可以实现 session 的共享与管理。

4 session与浏览器

Session被存储在服务器端,并通过Session ID(使用Cookie机制)传递给客户端。需要注意的是,在这种情况下此Cookie的生命周期仅为-1单位时间(即仅存在于浏览器内存环境中),因此当用户关闭浏览器时该Cookie就会被清除。

每当用户再次登录至浏览器并访问该服务器时,
就不会有sessionId传递给该系统。
从而使得网络设备误判用户的连接状态,
随后会导致设备自动建立新的连接 session。
因此系统将会主动向客户端返回新的 session ID,
并在后续的数据传输过程中正确处理请求与响应关系。

您可能会问:原来的session对象会如何处理?如果一个session长时间未被使用,则会被服务器自动删除。该时长在Tomcat中的默认设置为30分钟。需要注意的是,默认情况下可以在预设路径${CATALANA}/conf/web.xml中找到该配置参数。当然,在此之外您还可以根据需要在自己的web.xml文件中进行相应设置。

web.xml

** ** ** 30** ** **

session失效的时间提醒我们一个潜在的问题!当网页处于长时间未响应状态时(超过30分钟之后),再次尝试点击链接或提交表单会发现该session已经终止了。

5 session其他常用API

l String getId():获取sessionId;

l int getMaxInactiveInterval(): Retrieves the maximum inactivity interval (in seconds) for a session, with a default value of 30 minutes. When a session remains inactive for 30 minutes, then Tomcat will remove this session from the session pool;

void setInactiveInterval(int interval):此函数用于设定session的最大休息时间(以秒计)。当此休息时间设为1秒时,在该时间段内未被使用的session将被删除。

获取会话创建的时间戳(毫秒):该方法用于获取Session对象的创建/生成时间戳,默认采用系统当前的时间点作为基准。

public long getLastAccessedTime():获取会话的最后访问时间;返回当前时间以毫秒表示的最后一次会话访问时间;

void invalidate(): 使会话失效!当被调用时会导致会话失效。当客户端再次发起请求时, 服务器将创建一个新的会话, 并将新会话的sessionId返回给客户端.

l boolean isNew():判断session是否处于新状态。当客户端发起首次请求时,服务器会向其生成新的session;然而,在此时刻段内(即尚未收到浏览器响应之前),服务器尚未将sessionId返回给浏览器;此时该session的状态标记为'新'状态。

6 URL重写

已知session依赖Cookie, 那么, session为何依赖Cookie呢? 因为服务器每次请求都需要获取一个sessionId, 然后与客户端的session关联起来. 那么, 当客户端浏览器关闭Cookie时, 此时对应的session是否也就随之消失呢?

另外一种方法是通过URL重写实现每个请求携带sessionid。具体来说,在每个页面中对所有链接和表单进行处理,在其属性中新增一个名为jSessionId的隐藏参数,并将其值设置为当前sessionid。每当客户点击链接或提交表单时(实际上是指当客户点击链接或提交表单时),服务器可以通过获取该页面中的jSessionId参数来确定客户端的身份信息。

index.jsp

复制代码
 <body>

    
 	<h1>URL重写</h1>
    
 	<a href='/day06_5/index.jsp;jsessionid=<%=session.getId() %>'  >主页</a>
    
  
    
 	<form action='/day06_5/index.jsp;jsessionid=<%=session.getId() %>' method="post">
    
 	<input type="submit" value="提交"/>
    
 	</form>
    
 </body>

**
**

请特别注意,在index.jsp之后应使用分号而非问号,并 server对jsessionid parameter有 specific requirement

另外一种方式是通过response.encodeURL()来为每个请求处理其对应的URL。该方法会自动地添加jsessionid参数,并且其效果与我们手动添加的一致。

复制代码
 <a href='<%=response.encodeURL("/day06_5/index.jsp") %>' >主页</a>

    
  
    
 <form action='<%=response.encodeURL("/day06_5/index.jsp") %>' method="post">
    
 	<input type="submit" value="提交"/>
    
 </form>

使用response.encodeURL()更先进地处理请求,在此过程中它会检测客户端浏览器是否已禁用Cookie,并根据结果决定是否在URL后面追加jsessionid。

四、案例:一次性图片验证码

1 验证码有啥用

当我们进行注册操作而无需验证码时

检测手段能够区分请求来源是否为人类或机器人!然而具备智能的程序试图解析验证码图像。然而解析这样的图像并非易事,由于常见的验证码图像通常包含干扰线条,这些干扰线条使得人类难以辨认,因此相应的解析系统也难以准确识别

2 VerifyCode类

目前我们已开发出了cn.itcast.utils.verifyCode类这一功能模块,并且在之前的版本中已经实现了基础功能。该类能够生成验证码图片及其文字内容。以下让我们通过一个小示例来演示其使用方法。

复制代码
 public void fun1() throws IOException {

    
 		// 创建验证码类
    
 		VerifyCode vc = new VerifyCode();
    
 		// 1、获取随机图片
    
 		BufferedImage image = vc.getImage();
    
 		// 2、获取刚刚生成的随机图片上的文本
    
 		String text = vc.getText() ;
    
 		//System.out.println(text);
    
 		// 保存图片
    
 		FileOutputStream out = new FileOutputStream("F:/xxx.jpg");
    
 		VerifyCode.output(image, out); //把image图片保存到out流中!
    
  
    
 	}

3 在页面中显示动态图片

我们计划创建一个名为VerifyCodeServlet的服务端组件,在该组件中我们会生成动态验证码图片,并将其通过响应流发送至客户端显示。具体来说,在服务端会调用相关函数创建动态验证码图片,并通过响应流将此图片发送至客户端显示区域的标签内以实现自动验证功能。

VerifyCodeServlet

复制代码
 public class VerifyCodeServlet extends HttpServlet {

    
 	public void doGet(HttpServletRequest request, HttpServletResponse response)
    
 			throws ServletException, IOException {
    
 		VerifyCode vc = new VerifyCode();
    
 		BufferedImage image = vc.getImage();
    
 		String text = vc.getText();
    
 		System.out.println("text:" + text);
    
 		VerifyCode.output(image, response.getOutputStream());
    
 	}
    
 }

index.jsp

复制代码
 <script type="text/javascript">

    
     	function _change() {
    
     		var imgEle = document.getElementById("vCode"); 
    
     		imgEle.src = "/day06_6/VerifyCodeServlet?" + new Date().getTime(); 
    
     	}
    
     </script>
    
 ...  
    
   <body>
    
     <h1>验证码</h1>
    
     <img id="vCode" src="/day06_6/VerifyCodeServlet"/>
    
     <a href="javascript:_change()">看不清,换一张</a> 
    
   </body>

4 在注册页面中使用验证码

复制代码
     <form action="/day06_6/RegistServlet " method="post">

    
     	用户名:<input type="text" name="username"/><br/>
    
     	验证码:<input type="text" name="code" size="3"/>
    
     	<img id="vCode" src="/day06_6/VerifyCodeServlet"/>
    
     	<a href="javascript:_change()">看不清,换一张</a>
    
     	<br/>
    
     <input type="submit" value="Submit"/>
    
     </form>

5 RegistServlet

修改VerifyCodeServlet

复制代码
 public class VerifyCodeServlet extends HttpServlet {

    
 	public void doGet(HttpServletRequest request, HttpServletResponse response)
    
 			throws ServletException, IOException {
    
 		VerifyCode vc = new VerifyCode();
    
 		BufferedImage image = vc.getImage();
    
 		request.getSession().setAttribute("vCode", vc.getText());//在session中保存验证码文本
    
 		VerifyCode.output(image, response.getOutputStream());
    
 	}
    
 }

RegistServlet

复制代码
 public class RegistServlet extends HttpServlet {public void doPost(HttpServletRequest request, HttpServletResponse response)

    
 			throws ServletException, IOException {
    
 		request.setCharacterEncoding("utf-8");
    
 		response.setContentType("text/html;charset=utf-8");
    
 		
    
 		String username = request.getParameter("username");
    
 		String vCode = request.getParameter("code"); //获取表单中的验证码,这是用户在表单中填写的。
复制代码
    /*
复制代码
    获取session中的验证码,这是在生成验证码图片后,保存在session中的正确的验证码文本
复制代码
 */

    
 		String sessionVerifyCode = (String)request.getSession().getAttribute("vCode"); 
    
 		
    
 		if(vCode.equalsIgnoreCase(sessionVerifyCode) ) {  //比较用户输入的与真正的是否相同。
    
 			response.getWriter().print(username + ", 恭喜!注册成功!");
    
 		} else {
    
 			response.getWriter().print("验证码错误!");
    
 		}
    
 	}
    
 }

6 总结验证码案例

l VerifyCodeServlet:

验证码生成流程:Create a new instance of VerifyCode. Obtain the验证码 image from the generated instance.

在session期间记录验证码文本时,请执行以下操作:使用request.getSession.getAttribute获取vCode属性值,并结合vc.getText()获取当前验证码文本。

将验证码数据发送至页面:VerifyCode.output(image, response outputStream);

l regist.jsp:

Ø 表单中包含username和code字段;

在表单中设置verifyCodeServlet的标签指向VerifyCodeServlet,并用于页面上显示验证码图片;

Ø 提供“看不清,换一张”链接,指向_change()函数;

Ø 提交到RegistServlet;

l RegistServlet:

Ø 获取表单中的username和code;

Ø 获取session中的vCode;

Ø 比较code和vCode是否相同;

Ø 相同说明用户输入的验证码正确,否则输入验证码错误。

全部评论 (0)

还没有任何评论哟~