首页 > WEB开发 > 后台开发 > 会话管理(Cookie & HttpSession)
2014
08-14

会话管理(Cookie & HttpSession)

08. 会话管理(Cookie & HttpSession)

概述

1)什么是会话?可以简单的理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。

2)会话技术要解决的问题?如何保存会话中的数据并实现多次请求或会话中共享数据的问题:对每个用户来说可以共享多次请求中产生的数据,且不同用户产生的数据要相互隔离,如购物车数据(由上述特点知用户购买的商品不能保存在request或servletContext中)

3)两种会话技术:

Cookie(客户端技术):程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据(cookie)去,这样,web资源处理的就是用户各自的数据了。如下图:

08. 会话管理(Cookie & HttpSession)357

HttpSession(服务器端技术):服务器在运行时可以为每一个用户的浏览器创建一个其独享的HttpSession对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中。当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务。如下图:

08. 会话管理(Cookie & HttpSession)538

Cookie

1、与Cookie有关的HTTP协议消息头字段(原理):

请求消息头:Cookie;响应消息头:Set-Cookie

2、服务器读写Cookie

① 获取Cookie(读):request接口中定义了一个getCookies()方法,用于获取浏览器请求消息头中的Cookie字段。

② 发送Cookie(写):javax.servlet.http.Cookie类用于创建一个Cookie,response接口中定义了一个addCookie()方法,它用于在其响应头增加一个相应的Set-Cookie头字段。

注: 浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB

③ Cookie类中有如下常用方法:

public Cookie(name,value)

getName():当获得Cookie[]后,遍历数组找到指定名称的Cookie时需要用到。

setValue() 与getValue():获得指定名称的Cookie后,取得Cookie里的值用到。

setPath与getPath方法

setMaxAge与getMaxAge方法 (秒)

3、Cookie属性

1)name:Cookie的名称,必要的属性

2)value:Cookie的取值(不能为中文),必要的属性。在创建一个Cookie时,必须指定name和value(参考Cookie类的构造方法)

————— 以下为可选属性 —————

3)path:Cookie的路径(☆)

默认值:写Cookie的那个资源的访问路径。如http://localhost/myApp/servlet/demo1往客户端写入一个Cookie,则该Cookie的默认path属性值为:/myApp/servlet。(☆)可通过setPath()方法修改path属性值。

② 满足以下条件时浏览器才会带Cookie给服务器:当前访问的资源路径.startWith(客户端已存cookie的路径)。如:有一个存在浏览器缓存中的Cookie的path属性值为:myApp/servlet/,当用户访问http://localhost/myApp/demo2时,浏览器根本不带Cookie给服务器,浏览器比对的是cookie的路径和当前访问的资源的路径。

秒杀技:如果一个Cookie的路径设置为当前应用,则访问该网站的任何资源时浏览器都会带上该Cookie给服务器!设置方法:setPath(request.getContextPath())

4)maxAge:Cookie的最大生存时间,单位为秒。默认情况下Cookie是在浏览器进程的内存区中,关闭浏览器Cookie消失。将最大时效设为0则是命令浏览器删除该cookie(注意路径要一致)

5)domain:Cookie的域名。这个好理解,如上述网址的域名为localhost。

注:有了上面的domain + path + name后,就可以唯一确定一个Cookie了。

4、Cookie相关案例

1)显示上次访问时间

ShowLastTime用于向浏览器输出上次访问时间:

 ① 如果存在名为lastTime的Cookie,显示上次访问时间

② 不管存在该Cookie否,都要向浏览器写一个名为lastTime的Cookie,值为当前时间戳

public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
	response.setContentType("text/html;charset=utf-8");
	PrintWriter out = response.getWriter();
	out.write("您上次访问的时间是:");

	//获取名称为lastTime的Cookie,并将值显示为本地时间类型
	Cookie[] cookies = request.getCookies();
	for(int i = 0;cookies!=null && i < cookies.length;i ++){
		if("lastTime".equals(cookies[i].getName())){
			out.write(new Date(Long.parseLong(cookies[i].getValue())).toLocaleString());
		}
	}
	//增加一个清除链接
	out.write("<a href='"+request.getContextPath()+"/servlet/ClearTime'>清除时间</a>");

	//把当前的时间戳写回Cookie
	Cookie cookie = new Cookie("lastTime",System.currentTimeMillis()+"");
	cookie.setPath(request.getContextPath());
	cookie.setMaxAge(Integer.MAX_VALUE);
	response.addCookie(cookie);
}

2)登录时保存用户名

本例中用户登录界面用Servlet技术实现:LoginUIServlet,代码如下:

response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();

//定义username和remember(需要填充到表单中的值)
String username = "";
String remember = "";
Cookie[] cookies = request.getCookies();
for(int i = 0; cookies!=null && i < cookies.length;i ++){
	if("logInfo".equals(cookies[i].getName())){
		username = cookies[i].getValue();
		remember = "checked='checked'";
	}
}

//输出表单
out.write("<form action='"+request.getContextPath()+"/servlet/LoginServlet' method='post'>");
out.write("用户名:<input type='text' name='username' value='"+username+"'/><br/>");
out.write("密码:<input type='password' name='password'/><br/>");
out.write("<input type='checkbox' name='remember' "+remember+"/>记住用户名<br/>");
out.write("<input type='submit' value='提交' /><br/>");
out.write("</form>");

表单处理的LoginServlet代码如下:

String username = request.getParameter("username");
String remember = request.getParameter("remember");

//定义Cookie
Cookie cookie = new Cookie("logInfo",username);
cookie.setPath(request.getContextPath());
if(remember != null){
	//记住密码
	cookie.setMaxAge(Integer.MAX_VALUE);
}else{
	//不记住密码
	cookie.setMaxAge(0);
}
response.addCookie(cookie);

3)利用Cookie记录最近浏览的商品(以图书为例)

主要是显示书目和查看图书信息两个部分,在查看图书信息时将处理更新Cookie,ShowAllBooksServlet代码:

response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//显示所有图书
out.write("<h1>本站含有如下图书:</h1>");
Map<String , Book> books = BookDB.getBooks();
for(Map.Entry<String, Book> entry : books.entrySet()){
	out.write(entry.getValue().getName()+"&nbsp;&nbsp;<a href='"+request.getContextPath()+"/servlet/ShowDetailServlet?id="+entry.getKey()+"' >查看详情</a><br/>");
}
//最近浏览记录(从Cookie中取)
out.write("<hr /><b>以下是您的最近浏览记录:</b><br />");
Cookie[] cookies = request.getCookies();
for(int i = 0;cookies!=null && i < cookies.length;i ++){
	if("bookHistory".equals(cookies[i].getName())){
		String value = cookies[i].getValue();
		String[] ids = value.split("-");
		for(String id : ids){
			out.write(BookDB.getBookById(id).getName()+"<br/>");
		}
		break;
	}
}

ShowDetailServlet代码如下(如何处理Cookie):

response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();

//显示商品的详细内容
String id = request.getParameter("id");
Book book = BookDB.getBookById(id);
out.write(book.toString());

//存入Cookie
//makeId()方法会根据当前浏览书目id和历史浏览书目得到要存入Cookie中的数据,形如:3-2-1
String bookId = makeId(id,request);
Cookie cookie = new Cookie("bookHistory",bookId);
cookie.setPath(request.getContextPath());
response.addCookie(cookie);

HttpSession

1、Session的实现原理(Cookie)

在概述中我们知道,服务器端会为每个用户浏览器创建一个独享的HttpSession对象,开发人员可以调用request对象的getSession()方法得到session对象。那服务器是如何实现一个session为一个用户浏览器服务器的?看下图request.getSession()方法执行过程即可明白:

08. 会话管理(Cookie & HttpSession)5535

Session的基本原理即生成了一个名为JSESSIONID的Cookie,可以通过session的getId()方法查看JSESSIONID。

扩展:JSESSIONID实际上就是一个UUID(Universally Unique Identifier),在java.util包中提供了一个类直接生成UUID,如:String token = UUID.randomUUID().toString()。

 2、session相关API

1)request对象还有个getSession(boolean create)方法,若create为true,效果同getSession();若create为false,服务器只是查找,找到则返回,找不到不会创建新的,返回null。

2)session对象的默认失效时间是30分钟,session对象的invalidate()方法会使HttpSession对象立即失效。在web.xml文件中也可以配置Session的失效时间:

<session-config>
	<!-- 默认单位是分钟 -->
	<session-timeout>5</session-timeout>
</session-config>

3、HttpSession相关案例

1)使用Session完成简单的购物车功能

主要是显示书目和购买图书两个部分,在购买图书时将处理更新Cookie,ShowAllBooksServlet代码:

HttpSession session = request.getSession();//第一次调用getSession开始会话

response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();

//显示所有可购买的书目,提供购买链接
out.write("<h1>本站提供一下好书</h1>");
Map<String ,Book> books = BookDB.getBooks();
for(Map.Entry<String, Book> entry : books.entrySet()){
	String url = request.getContextPath()+"/servlet/BuyServlet?id=" + entry.getKey();
	url = response.encodeURL(url);
	out.write(entry.getValue().getName() + "&nbsp;&nbsp;<a href='"+url+"'>加入购物车</a><br/>");
}

//显示购物车商品
List<Book> cart = (List<Book>)session.getAttribute("cart");
if(cart == null || cart.size() == 0){
	out.write("不好意思,当前购物车为空~~");
}else{
	out.write("<b>购物车中有如下好书</b><br/>");
	for(Book book : cart){
		out.write(book.getName()+"<br/>");
	}
}

BuyServlet代码如下(如何处理Cookie):

response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();

String id = request.getParameter("id");
Book book = BookDB.getBookById(id);

//处理session
HttpSession session = request.getSession();
List<Book> cart = (List<Book>)session.getAttribute("cart");
if(cart == null){
	cart = new ArrayList<Book>();
	session.setAttribute("cart",cart);
}
cart.add(book);

//处理完毕后给用户响应
String url = request.getContextPath()+"/servlet/ShowAllBooksServlet";
url = response.encodeURL(url);
out.write("OK!"+book.getName()+"已放入购物车。<a href='"+url+"'>继续购物</a>");

//将JSESSIONID存入浏览器5分钟(关闭浏览器后还可以看到购物车数据)
Cookie cookie = new Cookie("JSESSIONID",session.getId());
cookie.setPath(request.getContextPath());
cookie.setMaxAge(5*60);
response.addCookie(cookie);

2)完善登陆表单(验证码后台校验)

加入验证码图片,在后台生成验证码图片时,会同时将验证码图片中的字符保存到session对象中,在处理登陆表单时,同时获得表单和session中的验证码字符并比对。

3)防止表单的重复提交

表单重复提交的概念不多说,如何防止用户的重复提交呢?用Servlet生成一个表单,在Servlet中为每次产生的表单分配一个唯一的UUID,并在form表单中增加一个隐藏域,值设置为这个UUID,同时在当前用户的Session域中保存这个UUID。代码如下:

response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();

String token = UUID.randomUUID().toString();
request.getSession().setAttribute("token", token);

out.write("<form action='"+request.getContextPath()+"/servlet/RegisterServlet' method='post'>");
out.write("姓名:<input type='text' name='name'/><br />");
out.write("<input type='hidden' name='token' value='"+token+"'/>");
out.write("<input type='submit' value='保存' />");
out.write("</form>");

当用户提交FORM表单时,负责处理表单提交的serlvet得到表单提交的标识号,并与session中存储的标识号比较,如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号,代码如下:

request.setCharacterEncoding("utf-8");
String name = request.getParameter("name");

//分别得到Session域和表单中的token
String stoken = (String)request.getSession().getAttribute("token");
String ftoken = request.getParameter("token");

if(ftoken.equals(stoken)){
	System.out.println(name);
	request.getSession().removeAttribute("token");
}else{
	response.getWriter().write("ya mie die~~");
}

4、HttpSession对象的状态(☆)

State of HttpSession

由于session对象可能会被持久化,对于session.setAttribute(String,Object)中存储的值,必需要实现Serializable接口,否则会报异常。【持久化存储的文件在Tomcat安装目录下的work\Catalina\localhost\appName里】

5、客户端禁用Cookie后会话数据的保持

当客户端禁用Cookie后,永远不会向服务器端带任何cookie。解决办法:

①文字提示(乌龙):文字提示:请不要禁用您的Cookie / 连接超时……

②URL重写:会在访问的地址后面加上“;JSESSIONID=xxx”的字段,URL重写有如下两种方式:

response.encodeRedirectURL(String url):用于对sendRedirect方法中的url地址进行重写。

response.encodeURL(String url):用于对表单action和超链接的url地址进行重写。

注:URL重写必须对网站的所有网址都重写,成本较高。重写后的网址形式如:http://localhost:8080/servlet/ServletDemo1;JSESSIONID=123


留下一个回复

你的email不会被公开。