会话跟踪
1.无状态的HTTP协议
互联网通信协议主分为两大类,无状态的协议和有状态的协议,两者最大的差别在于客户端与服务器之间维持联机上的不同。HTTP协议就是一种无状态的协议。
当客户端向服务器端发出请求的时候,两端才会建立连接。为了节约资源两端的请求结束后将会暂时断开连接。对于web应用,保持状态非常重要,一个有用的状态可以用来帮助在多个请求和响应之间实现复杂的逻辑关系。
2.会话跟踪技术
上一次所传递的数据能够维持状态到下一个请求,并且辨别是否是同一个客户发出。
1).Cookie技术
Cookie是指某些网站为了辨别用户的身份而储存在用户终端上的文本信息(通常是通过加密处理的),浏览器第一次向服务器发送请求,服务器会向浏览器响应一个Cookie,这个Cookie是独一无二的。当浏览器在次向服务器发送请求时,会携带这个Cookie。以此来区分是否是同一个用户。
Cookie的创建:
Cookie cookie = new Cookie(“name”,”123465”);
参数name 表示Cookie的属性
参数123465 表示值
服务器端可以通过 response.addCookie(cookie);的方式响应回到客户端。 同时request可以通过getCookie()方法来获取Cookie。返回的是网站所有的Cookie的对象数组。
Cookie的setPath()方法可以重新指定其他访问路径,比如可以将其设置为在某个应用下路径的共享,或者在同一服务器的所有应用共享。
如:cookie.setPath(“/application/jsp”);
Cookie.setPath(“/”);
cookie有一定的存活时间,不会再客户端一直保存,一般cookie保存在浏览器内存中,当浏览器关闭的时候就会失效。这种Cookie也叫临时cookie,使用Cookie对象的setMaxAge()方法设置其存活的时间。(以秒为单位的)
下面代码演示使用Cookie记录用户最后一次访问的时间和访问的次数。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String nowTime = sdf.format(new Date());
String lastVistTime = "";
int vistedCount = 0;
// 获取客户端浏览器保存的所有Cookie
Cookie[] cookies = request.getCookies();
if (cookies != null)
for (Cookie cookie : cookies) {
// 判断是否为记录最近访问时间的Cookie
if ("lastVistTime".equals(cookie.getName())) {
lastVistTime = cookie.getValue();
}
// 判断是否为记录访问次数的Cookie
if ("vistedCount".equals(cookie.getName())) {
vistedCount = Integer.valueOf(cookie.getValue());
}
}
// 若曾经访问过,输出上次访问时间
if (!"".equals(lastVistTime))
out.println("您上一次的访问时间是:" + lastVistTime);
// 输出访问次数
out.println("您是第" + (vistedCount + 1) + "次访问本网站");
// 以本次访问时间重建同名新Cookie
Cookie lastVistTimeC = new Cookie("lastVistTime", nowTime);
// 设置最大存活时间:一年
lastVistTimeC.setMaxAge(365 * 24 * 60 * 60);
// 以新访问次数重建同名新Cookie
Cookie visitCountC = new Cookie("vistedCount",
String.valueOf(vistedCount + 1));
// 设置最大存活时间:一年
visitCountC.setMaxAge(365 * 24 * 60 * 60);
// 将上述新建Cookie响应到客户端
response.addCookie(lastVistTimeC);
response.addCookie(visitCountC);
2)Session技术
Session是服务器端的会话跟踪服务,HttpSessin接口的实例,可以用来保存用户请求时的一些信息,服务器在无状态HTTP协议下用来识别和维护具体的某个用户。
Session是第一次请求服务器时,由服务器创建,在此期间通过invalidate()方法或者超过最大活动期间,就会失效。在此客户与浏览器属于同一个会话。
创建会话时会分配唯一一个会话标识:SessionID以“JSESSIONID”的属性名保存在客户端的Cookie中在用户的请求中,服务器通过读取Cookie中的JSESSIONID的值来识别不同的用户,从而实现对每个用户的会话跟踪。
方法 |
描述 |
getSession() |
获取与客户端请求关联的当前的有效的Session,若没有Session关联则新创建一个。 |
getSession(boolean create) |
获取有效的Session,若没有则关联失败,当参数为真时,Session被新建,为假时返回空值。 |
存取会话域属性和管理会话生命周期的方法:
方法 |
描述 |
setAttribute(String key,Object value) |
以key/value的形式保存在Session对象中 |
getAttribute(String key) |
通过key获取对象值 |
removeAttribute(String key) |
通过key删除 |
Invalidate() |
设置HTTPsession对象失效 |
setMaxInactiveInterval(int interval) |
设置Http对象的非活动时间,若超过这个时间,则会失效 |
getMaxInactiveInterval |
获取有效非活动时间(以秒为单位) |
String getId() |
获取session对象的标识sessionid |
long getCreationTime() |
获取session对象产生的时间单位为毫秒 |
Long getLastAccessedTime() |
获取用户最后这个session对象请求的时间 |
储存会话域的属性“username”,值为123465:
Session.setAttribute(“username”,“123456”);
通过属性名获取值:
String uname = session.getAttribute(“username”);
通过属性名将属性从会话域中移除:
Session.removeAttribute(“username”);
在web.xml中设置会话最大不活动时间
<session-config>
<session-timeout>10</session-timetion>单位为分钟
</session-config>
服务器在执行会话失效代码后,会清除会话对象及其所有会话域属性,同时响应客户端浏览器清除Cookie中的JSESSIONID。在实际应用中,此方法多用来实现系统的“安全退出”,使浏览器彻底结束此次会话,清除所有会话的相关信息,防止会话劫持等黑客攻击。
注意!!!!我们一般认为只要关闭了浏览器,会话就会结束,其实并不是,他客户端浏览器的cookie结束,若会话的最大不活动时间还未结束,储存在服务器端的会话对象任然还存在。
下面进行使用session实现一个购物车:
<%@ 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>书籍选购</title>
</head>
<body>
<h2>请选择您要购买的书籍:</h2>
<form action="ShoppingCarServlet" method="post">
<p><input type="checkbox" name="book" value="JavaSE应用与开发">JavaSE应用与开发</p>
<p><input type="checkbox" name="book" value="JavaWeb应用与开发">JavaWeb应用与开发</p>
<p><input type="checkbox" name="book" value="JavaEE应用与开发">JavaEE应用与开发</p>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
*****************************************************
@WebServlet("/ShoppingCarServlet")
public class ShoppingCarServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 获取会话对象
HttpSession session = request.getSession();
// 从会话域中获取shoppingCar属性对象(即:购物车)
// 对象定义为Map类型,key为书名,value为购买数量
Map<String, Integer> car = (Map<String, Integer>) session
.getAttribute("shoppingCar");
// 若会话域中无shoppingCar属性对象,则实例化一个
if (car == null) {
car = new HashMap<String, Integer>();
}
// 获取用户选择的书籍
String[] books = request.getParameterValues("book");
if (books != null && books.length > 0) {
for (String bookName : books) {
// 判断此书籍是否已在购物车中
if (car.get(bookName) != null) {
int num = car.get(bookName);
car.put(bookName, num + 1);
} else {
car.put(bookName, 1);
}
}
}
// 将更新后的购物车存储在会话域中
session.setAttribute("shoppingCar", car);
response.sendRedirect("ShoppingListServlet");
}
}
*******************************************************
@WebServlet("/ShoppingListServlet")
public class ShoppingListServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
HttpSession session = request.getSession();
Map<String, Integer> car = (Map<String, Integer>) session
.getAttribute("shoppingCar");
if (car != null && car.size() > 0) {
out.println("<p>您购买的书籍有:</p>");
// 遍历显示购物车中的书籍名称和选择次数
for (String bookName : car.keySet()) {
out.println("<p>" + bookName + " , " + car.get(bookName)
+ " 本</p>");
}
} else {
out.println("<p>您还未购买任何书籍!</p>");
}
out.println("<p><a href='bookChoose.jsp'>继续购买</a></p>");
}
}
*******************************************************
3)URL重写技术
URL重写技术是指服务器程序对接受的URL请求重新写成网站可以处理的另一个URL的过程。当不能确定客户端浏览器是否支持cookie的情况下,使用URL重写技术可以对请求的URL地址追加会话标识,从而实现用户的会话跟踪。
对如下请求:
http://localhost:8080/chapter/EncodeURLServlet
经过URL重写后,地址格式变为:
http://localhost:8080/chapter/EncodeURLServlet;jsessionid=24666BB454BFFD64567DS16S5
其中的jsessionid是会话标识,服务器即是通过它来标识某个用户的访问。
URL重写通过Response的enccodeURL()方法和wncodeRedirectURL()方法实现,URL重写方法根据请求信息中是否包含Set-Cookie请求头来决定是否进行URL重写。若包含该请求头,会将URL原样输出;若不包含,则将会话标识写到URL中。
4)隐藏表单域
利用form表单的隐藏表单域,可以在完全脱离浏览器对Cookie的使用限制以及在用户无法从页面显示看到隐藏标识的情况下,将标识随请求一起传送给服务器处理,从而实现会话的跟踪。
在form中:
<input type=”hidden” name = “userID” value=”10010”>
隐藏域的获取:String flag = request.getParameter(“userID”);
隐藏域只能通过form表单来实现
- 总结
- HTTP协议是一种无状态的协议,不会一直与客户端保持联机状态
- Cookie是指某网站为了辨别用户身份而储存在用户终端的文本信息
- 通过cookie,服务器接收到来自客户端浏览器的请求时,能够通过分析请求头的内容而得到客户端特有的信息,从而动态的生成与该用户对应的内容
- Session技术是使用 httpSession对象实现会话跟踪的技术
- Session用来保存单个用户访问时的信息,是辨别和维护具体某个用户的方式
- 当不能确定用户客服端浏览器是否可以使用cookie的情况下,使用URL重写技术。
- 利用form表单的隐藏域,可以脱离浏览器对cookie的使用限制以及在用户从页面显示看到隐藏标识的情况下,将标识随请求一起传送给服务器处理。