4.3 会话跟踪
4.3.1 Cookie
HTTP协议是一个无状态的协议,无状态也就是说,如果此时的状态是连接的,下一刻状态就可能是断开的,状态是不稳定的。
Cookie是在HTTP协议下,服务器或脚本可以维护客户工作站信息的一种方式。Cookie是由web服务器保存在用户浏览器(客户端)上的小文本文件,可以包含有关用户的信息。无论何时用户链接到服务器,web站点都可以访问Cookie信息。
目前Cookie有些是临时的,有些是持续的。临时的只在浏览器上保存一段时间,一旦超过规定时间就会被系统清除。持续的Cookie保存在用户的Cookie文件中,下一次用户返回时,仍然可以对它进行调用。有些用户会担心这样会造成信息泄露,其实网站以外的用户无法跨过网站来获得Cookie信息,Cookie的存在还是大有作用的。
Cookie类的方法列表:
举例:Cookie.java判断本地是否存在一个指定名称的cookie,没有则创建这个cookie并显示出来
package cy.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class Cookie
*/
@WebServlet("/Cookie")
public class Cookie extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public Cookie() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.setContentType("text/html;charset=UTF-8");
javax.servlet.http.Cookie cookie=null;
javax.servlet.http.Cookie[] cookies=request.getCookies();
boolean newCookie=false;
if(cookies!=null) {
for(int i=0;i<cookies.length;i++) {
if(cookies[i].getName().equals("cyCookie")) {
cookie=cookies[i];
}
}
}
if(cookie==null) {
newCookie=true;
int maxAge=5;
cookie=new javax.servlet.http.Cookie("cyCookie","first");
//设置Cookie路径
cookie.setPath(request.getContextPath());
cookie.setMaxAge(maxAge);
response.addCookie(cookie);
}
response.setContentType("text/html;charset=UTF-8");
java.io.PrintWriter out=response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title></title>");
out.println("</head>");
out.println("<body>");
out.println("Cookie的值为:"+cookie.getValue()+"<br>");
if(newCookie) {
out.println("<br>这里的信息只有第一次运行可以看到!<br>");
out.println("Cookie的生命周期为:"+cookie.getMaxAge()+"<br>");
out.println("Cookie的名字为:"+cookie.getName()+"<br>");
out.println("Cookie的路径为:"+cookie.getPath()+"<br>");
}
out.println("</body>");
out.println("</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);
}
}
其中setMaxAge()方法是设置Cookie的有效期,这里的单位是秒。在上面的例子中,浏览器运行结果为:
如果在5秒内刷新页面就会发现界面内容少了一些:
5秒后再刷新,页面又变成初次运行的结果。
4.3.2 URL参数传递与重写
我们可以随便在浏览器上搜索某个事物,会发现很多情况下浏览器地址栏除了显示正常的网页地址外,总会在后面跟一个?或!。这里就使用了URL参数传递的技术。
这个技术和前面介绍的cookie都是一种保持状态传递信息的技术。由于cookie技术是把上网信息保存在客户端的硬盘中,有一定安全隐患,URL传递参数就成为一种很好地替代。
不同开发平台,写法有所差异,在Java EE平台的语法形式为:
http://www.npumd.edu.cn/file.htm?id=12345&pw=6669
?代表要在URL后面加入的参数,如果是多个参数,则用&连接。这些参数传递到相关文件后,可通过request.getParameter()方法获取。
举例:传参文件 SetParam.java
package cy.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class SetParam
*/
@WebServlet("/SetParam")
public class SetParam extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public SetParam() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.setContentType("text/html;charset=UTF-8");
PrintWriter out=response.getWriter();
try {
String uname="Hedy";
String uage="19";
String encodedUrl=response.encodeURL(((HttpServletRequest) request).getContextPath()+
"/PlayerInfo?name="+uname+"&age="+uage);
out.println("<html");
out.println("<head>");
out.println("<title></title>");
out.println("</head>");
out.println("<body>");
out.println("查看信息请点击<a href=\""+encodedUrl+"\">这里</a>");
out.println("</body>");
out.println("</html");
}finally {
out.close();
}
}
/**
* @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);
}
}
接收文件PlayerInfo.java
package cy.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class PlayerInfo
*/
@WebServlet("/PlayerInfo")
public class PlayerInfo extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public PlayerInfo() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.setContentType("text/html;charset=UTF-8");
request.setCharacterEncoding("UTF-8");
PrintWriter out=response.getWriter();
try {
out.println("<html");
out.println("<head>");
out.println("<title></title>");
out.println("</head>");
out.println("<body>");
out.println("名字:"+request.getParameter("name"));
out.println("年龄:"+request.getParameter("age"));
out.println("</body>");
out.println("</html");
}finally {
out.close();
}
}
/**
* @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);
}
}
截图:
虽然我们在实际上网过程中看到了?,但是后面的内容似乎都看不太懂,这是因为Java EE给出了一种名为URL重写的技术。这个技术不是传递参数,而是获得一个进入的URL请求,然后把它重新写成网站可以处理的另一个URL。例如将/PlayerInfo.java?id=100111重写为/PlayerInfo.html。
URL重写的好处:
1)方便用户访问,同时屏蔽了一些重要信息
2)可以把动态的页面变成静态的,有利于搜索引擎的识别抓取
3)提高重用性。例如,系统更改了后端控制程序访问的方法,而通过URL重写定义的前端地址可以不用改,这样就提高了网站的移植性。
4.3.3 Session
Session是一个高级接口,是建立在上面两个技术之上的。它针对会话跟踪的底层实现机制,对用户是透明的。Session可以连续跨越多个用户的连接,通过HttpServletRequest的getSession()方法可以获取HttpSession对象:
HttpSession session=request.getSession();
使用Session来存储会话信息的步骤如下:
1)获取一个HttpSession的对象资源
2)判断是否存在指定的Session属性,如果存在则获取这个属性的值,不存在则创建这个属性
3)使用这个session对象的属性
4)如果不再需要这个对象,手动停止他或者什么都不做,等待系统自动回收
举例:Session.java
package cy.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
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;
/**
* Servlet implementation class SetParam
*/
@WebServlet("/SetParam")
public class Session extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public Session() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.setContentType("text/html;charset=UTF-8");
PrintWriter out=response.getWriter();
HttpSession session=request.getSession();
Date crtTime=new Date(session.getCreationTime());//创建对象的时间
Date lastAccessTime=new Date(session.getLastAccessedTime());//上次访问session的时间
Integer visitCount=new Integer(0);//统计访问次数
String visitCountKey=new String("visitCount");
String userIDKey=new String("userID");
String userID=new String("ABCD");
if(session.isNew()) {
//为空会自动创建一个session对象
session.setAttribute(userIDKey, userID);
}
else {
visitCount=(Integer)session.getAttribute(visitCountKey);
if(visitCount!=null) {
visitCount+=1;
}
if(session.getAttribute(userIDKey)!=null) {
userID=(String)session.getAttribute(userIDKey);
}
}
session.setAttribute(visitCountKey, visitCount);
response.setContentType("text/html;charset=UTF-8");
try {
out.println("<html");
out.println("<head>");
out.println("<title></title>");
out.println("</head>");
out.println("<body>");
out.println("<table border='1' align='center'>");
out.println("<tr><th>session</th><th>value</th></tr>");
out.println("<tr><td>id</td><td>"+session.getId()+"</td></tr>");
out.println("<tr><td>creation time</td><td>"+crtTime+"</td></tr>");
out.println("<tr><td>last access time</td><td>"+lastAccessTime+"</td></tr>");
out.println("<tr><td>User ID</td><td>"+userID+"</td></tr>");
out.println("<tr><td>number of visit</td><td>"+visitCount+"</td></tr>");
out.println("</body>");
out.println("</html");
}finally {
out.close();
}
}
/**
* @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);
}
}
第一次访问时访问次数为0,再访问几次结果为:
其他文件获取session的方式都是通过getAttribute()方法,但是在此之前一定要先申请获取session对象资源HttpSession session=request.getSession()才可以。
4.3.4 Servlet的上下文
运行在Servlet服务器中的web应用都会有一个全局的、储存信息的对象,这个对象就是Servlet的上下文,可以使同一个web应用中不同资源之间进行信息共享。Javax.Servlet.ServletContext接口就是对上下文对象进行相关操作的,通过它的getServletContext()方法,可以获得当前运行的Servlet的上下文对象。
Servlet可以通过名称将对象属性绑定到上下文,任何绑定到上下文的属性都可以被同一个web应用的其他Servlet使用。获取上下文实例及添加信息的主要方法为:
1)getServletContext():通过ServletConfig接口获得上下文实例,这里的上下文实例对象不是创建一个新的对象,而是去获取每个web应用都唯一对应的上下文对象
2)getInitParameter()、getInitParameterNames():访问web应用的初始化参数和属性
3)setAttribute()、getAttribute():添加并获取上下文对象的信息
4)getAttributeNames()、removeAttribute():获取上下文信息的名称,并移除上下文中保存的信息
举例:GetMessage.java获取上下文信息并在里面保存一个信息
package cy.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class PlayerInfo
*/
@WebServlet("/PlayerInfo")
public class GetMessage extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public GetMessage() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.setContentType("text/html;charset=UTF-8");
request.setCharacterEncoding("UTF-8");
PrintWriter out=response.getWriter();
try {
String info="Hedy冲鸭";
getServletConfig().getServletContext().setAttribute("Message", info);
out.println("<html");
out.println("<head>");
out.println("<title></title>");
out.println("</head>");
out.println("<body>");
out.println("信息:"+info);
out.println("</body>");
out.println("</html");
}finally {
out.close();
}
}
/**
* @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);
}
}
运行结果:
ShowMessage.java读取上一个文件的信息
package cy.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class PlayerInfo
*/
@WebServlet("/PlayerInfo")
public class ShowMessage extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public ShowMessage() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.setContentType("text/html;charset=UTF-8");
request.setCharacterEncoding("UTF-8");
PrintWriter out=response.getWriter();
try {
String getInfo=(String) getServletContext().getAttribute("Message");
out.println("<html");
out.println("<head>");
out.println("<title></title>");
out.println("</head>");
out.println("<body>");
out.println("读到的信息:"+getInfo);
out.println("</body>");
out.println("</html");
}finally {
out.close();
}
}
/**
* @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);
}
}
运行可以发现读到的信息和之前保存的信息是一样的:
通过上面的例子,可以发现上下文对象和session的方式非常相似,但是它们是有区别的,最根本的是它们具有不同的生命周期。
当web服务器开始运行后,这个应用就具备了唯一一个上下文对象,只要这个应用没有停止,这个上下文对象就一直存在。而session,每个用户都可以拥有一个。从适用范围来看,session应该是局部的,与用户有关的信息,上下文对象则是全局的,相对公共、更加安全的信息。