关于servlet会话跟踪,一搜都能搜出很多。我也不免落入俗套,也总结了一把。希望我所总结的知识尽量是知识海洋里的一汪清泉。能帮助到我自己和哪怕一个人,那也是值得的。
故事由来:
我们知道,http协议是个无状态的协议,所谓无状态就是指此时刻我们的状态是保持连接,下一刻我们的状态可能就是断开连接,状态是不稳定的,这就导致很多用户在上网时遇到问题,比如购物,我添加几次商品到购物车,如果没有会话跟踪,那么这些商品是没办法添加到一个购物车中的。再比如登录,每次访问同一个网站时,我每次都要输入用户名和密码,在同一个网站中,每个界面都要输入一次用户名和密码才能继续做其他事情。这就很尴尬了,所以,有会话跟踪技术的机制就诞生了。
故事梗概:
维持会话技术的4大法宝
法宝1:Cookie
cookie在浏览器允许使用的状态下,当客户请求服务器后,服务器会发送一个含有唯一的sessionID的cookie给客户端,并保存到客户端本地,下次客户端再和服务器交互时,通过在request头中加入cookie中的信息,服务器能辨识出客户身份,从而维持会话。
法宝2:隐藏的表单字段
在<input>中添加一个隐藏的字段,比如这样:
<input type="hidden" name="sessionid" value="12345">
那么在提交表单时,会把这个隐藏的名为sessionID的值为12345的域提交在GET或者POST数据中,服务器收到我的请求后,会给一个客户端一个cookie中有
Cookie:JSESSIONID=E6047C8DB41EEA107256ECB443640C49
每次向服务器请求时,就带着我的JSESSIONID,服务器会认识我的JSESSIONID给我发送我需要的请求。
法宝3:URL重写
在每个 URL 末尾追加一些额外的数据来标识 session 会话,服务器会把该 session 会话标识符与已存储的有关 session 会话的数据相关联。session 会话标识符被附加为 sessionid=12345,标识符可被 Web 服务器访问以识别客户端。
介绍完上面3个,下面是隆重登场的主角了。
法宝4:使用HttpSession
每一个事物产生都有它的作用,session也是,session由servlet容器产生,是一种会话跟踪技术,能解决用户在登录或者购物时多页面请求或存储相关用户信息。
比如这么个场景:你在打开淘宝,然后看到个想买的商品后,你点击立即购买,但是这时候会跳到登录那个新的界面,然后你登录之后,什么事都不用做,再次回到那个商品购买界面刷新下,你就发现这时候你再买这个商品就可以了。这就是session的作用。
多说一句:上面说到的cookie,其中就包含有session的ID,session是借助cookie来实现会话跟踪的,所以,如果cookie被删或者禁用的话,session这个会话跟踪技术也就无法使用了。
故事真相:
session的前世今生:
这是后来补的,因为之前对session的理解有偏差,所以这里纠正下。由问答形式给出。
session由谁创建,怎么创建?
session由服务器端创建,创建方式:HttpSession session = request.getSession(true)。
session在哪保存?
session保存在服务器端,session创建好会由Tomcat的StandardManager类将session存储在服务器内存中,也可以持久化到DB,file,redis中。
session既然由服务器创建,那么在Tomcat中如何创建的?
在tomcat服务器中由ManagerBase类提供创建方法:随机数+时间+jvmid。
session有哪些接口?
创建好session后就可以使用session的API了,API在下面给出。
客户端会保存session吗?
客户端(浏览器端)是不会保存session的,session在服务器端保存,客户端只保存一个sessionid到cookie中,而不会保存session。
session最终是怎么销毁的?
通过session.invalidate、超时、关闭服务器中三者之一。单纯关闭浏览器session不会消失,因为session不在浏览器端保存,而在服务器端。
session的API如下:
public Object getAttribute(String name)
该方法返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返回 null。
public Enumeration getAttributeNames()
该方法返回 String 对象的枚举,String 对象包含所有绑定到该 session 会话的对象的名称。
public long getCreationTime()
该方法返回该 session 会话被创建的时间,自格林尼治标准时间 1970 年 1 月 1 日午夜算起,以毫秒为单位。
public String getId()
该方法返回一个包含分配给该 session 会话的唯一标识符的字符串。
public long getLastAccessedTime()
该方法返回客户端最后一次发送与该 session 会话相关的请求的时间自格林尼治标准时间 1970 年 1 月 1 日午夜算起,以毫秒为单位。
public int getMaxInactiveInterval()
该方法返回 Servlet 容器在客户端访问时保持 session 会话打开的最大时间间隔,以秒为单位。
public void invalidate()
该方法指示该 session 会话无效,并解除绑定到它上面的任何对象。
public boolean isNew()
如果客户端还不知道该 session 会话,或者如果客户选择不参入该 session 会话,则该方法返回 true。
public void removeAttribute(String name)
该方法将从该 session 会话移除指定名称的对象。
public void setAttribute(String name, Object value)
该方法使用指定的名称绑定一个对象到该 session 会话。
public void setMaxInactiveInterval(int interval)
该方法在 Servlet 容器指示该 session 会话无效之前,指定客户端请求之间的时间,以秒为单位。
下面说的是session和session的ID,不同的访问者对不同的session和sessionID,
举例如下,一个test.html,一个testSession.java,一个google chrome,一个火狐浏览器。
test.html文件如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<form action="http://127.0.0.1:8081/HelloServlet/testSession" method="post">
<input type="text" name="username" />
<input type="submit" />
</form>
</body>
</html>
testSession.java文件如下(.java文件使用的是eclipse下创建的servlet文件)
package com.hundsun.vivizhang.servlet; import java.io.IOException; import java.io.PrintWriter;
import java.util.Date; import javax.jws.WebService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; @WebServlet("/testSession")
public class testSession extends HttpServlet{
private static final long serialVersionUID = 1L; /**
* @see HttpServlet#HttpServlet()
*/
public testSession() {
super();
// TODO Auto-generated constructor stub
} /**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Create a session object if it is already not created.
HttpSession session = request.getSession(true);
// Get session creation time.
Date createTime = new Date(session.getCreationTime());
// Get last access time of this web page.
Date lastAccessTime =
new Date(session.getLastAccessedTime()); String title = "Welcome Back to my website";
Integer visitCount = new Integer(0);
String visitCountKey = new String("visitCount");
String userIDKey = new String("userID");
String userID = new String("ABCD"); // Check if this is new comer on your web page.
if (session.isNew()){
title = "Welcome to my website";
session.setAttribute(userIDKey, userID);
} else {
visitCount = (Integer)session.getAttribute(visitCountKey);
visitCount = visitCount + 1;
userID = (String)session.getAttribute(userIDKey);
}
session.setAttribute(visitCountKey, visitCount); // Set response content type
response.setContentType("text/html");
PrintWriter out = response.getWriter(); String docType =
"<!doctype html public \"-//w3c//dtd html 4.0 " +
"transitional//en\">\n";
out.println(docType +
"<html>\n" +
"<head><title>" + title + "</title></head>\n" +
"<body bgcolor=\"#f0f0f0\">\n" +
"<h1 align=\"center\">" + title + "</h1>\n" +
"<h2 align=\"center\">Session Infomation</h2>\n" +
"<table border=\"1\" align=\"center\">\n" +
"<tr bgcolor=\"#949494\">\n" +
" <th>Session info</th><th>value</th></tr>\n" +
"<tr>\n" +
" <td>id</td>\n" +
" <td>" + session.getId() + "</td></tr>\n" +
"<tr>\n" +
" <td>Creation Time</td>\n" +
" <td>" + createTime +
" </td></tr>\n" +
"<tr>\n" +
" <td>Time of Last Access</td>\n" +
" <td>" + lastAccessTime +
" </td></tr>\n" +
"<tr>\n" +
" <td>User ID</td>\n" +
" <td>" + userID +
" </td></tr>\n" +
"<tr>\n" +
" <td>Number of visits</td>\n" +
" <td>" + visitCount + "</td></tr>\n" +
"</table>\n" +
"</body></html>");
} /**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
此处不用配置web.xml文件。
实验开始,先清掉浏览器的缓存,然后在浏览器中输入 localhost:8080/HelloServlet/test.html ,我的图中用的是127.0.0.1的环路地址,是一样的哈。
输入完后,打开chrome的F12按键,敲击回车,访问到页面,得到的不含cookie的F12中的截图(图1)是这样的。
图1
然后输入一些信息,点击提交后,F12调试页面是这样的(图2)。
图2
点击提交后跳转到这个界面(图3)
图3
然后就能得到下面的结论:
(1)同一浏览器相同窗口刷新后,访问的是同一sessionID,如图4所示,刷新1次,显示访问次数为2.
(2)同一浏览器不同窗口访问,访问的是同一sessionID,如图4所示,开两个窗口,但是sessionId仍然是E04B...065.
图4
(3)同一浏览器关闭后再次访问也不是同一session,如图5所示,切换到firefox后id和visitCount都不同了。
图5
(4)不同的浏览器访问是不同session和sessionID。如图6所示。
图6
故事总结:
这个故事说的是会话跟踪的技术,以后在使用Servlet时,要学会使用,这是一个非常重要的技术,但是也听到有反对使用cookie的,这些都不是重点,重点是这个技术是现在web开发必不可少的。