目录
前言
客户端的每次请求,服务器都会产生一个HttpServletRequest对象,该对象只保存请求所传递的数据。用一个WEB应用共享一个ServletContext对象,所以当多个用户登录时就有可能会造成数据混淆。为了解决这个问题,Servlet提供了会话跟踪技术来追踪用户状态,简单的说就是指将用户操作过的重要业务步骤记录下来,以便在后续的处理中使用。
会话跟踪是一种灵活、轻便的机制,它使Web上的状态编程变为可能。当用户在同一网站的多个页面之间转换时,根本无法确定是否是同一个客户,会话跟踪技术就可以解决这个问题。当一个客户在多个页面间切换时,服务器会保存该用户的信息。
有四种方法可以实现会话跟踪技术:URL重写、隐藏表单域、Cookie、Session。
一、会话、会话跟踪技术
(一)会话
会话指的是指的是从客户端打开与服务器的连接并发出请求到服务器响应客户端请求的全过程。简单来讲就是一个终端用户(服务器)与交互系统(客户端)进行通讯的全过程。
(二)会话跟踪技术
会话跟踪技术是对同一个用户对服务器的连续请求和接收响应的监视。简单来说就是为了数据共享,将用户与同一用户发出的不同请求之间关联起来。
二、常见的会话跟踪技术
常见的会话跟踪技术主要有4种:
1、Cookie(本地):键值对
HTTP协议是一个无状态协议,即Web应用程序无法区分收到的两个HTTP请求是否是同一个浏览器发出的。为了跟踪用户状态,服务器可以向浏览器分配一个唯一ID,并以Cookie的形式发送到浏览器,浏览器在后续访问时总是附带此Cookie,这样,服务器就可以识别用户身份。
一个 Cookie 是一个小的,已命名数据元素。Cookie是Web服务器发送给客户端的一小段信息,客户端请求时可以读取该信息发送到服务器端,进而进行用户的识别。
对于客户端的每次请求,服务器都会将Cookie发送到客户端,在客户端可以进行保存,以便下次使用。与其它技术比较,Cookie 的一个优点是在浏览器会话结束后,甚至在客户端计算机重启后它仍可以保留其值。
客户端可以采用两种方式来保存这个Cookie对象,一种方式是 保存在客户端内存中,称为临时Cookie,浏览器关闭后,这个Cookie对象将消失。另外一种方式是保存在客户机的磁盘上,称为永久Cookie。此后客户端只要访问该网站,就会将这个Cookie再次发送到服务器上,前提是这个Cookie在有效期内。 这样就实现了对客户的跟踪。
服务器识别Session的关键就是依靠一个名为JSESSIONID的Cookie。在Servlet中第一次调用req.getSession()时,Servlet容器自动创建一个Session ID,然后通过一个名为JSESSIONID的Cookie发送给浏览器:
如果我们要读取Cookie,例如,在IndexServlet中,读取名为lang的Cookie以获取用户设置的语言,可以写一个方法如下:
private String parseLanguageFromCookie(HttpServletRequest req) {
// 获取请求附带的所有Cookie:
Cookie[] cookies = req.getCookies();
// 如果获取到Cookie:
if (cookies != null) {
// 循环每个Cookie:
for (Cookie cookie : cookies) {
// 如果Cookie名称为lang:
if (cookie.getName().equals("lang")) {
// 返回Cookie的值:
return cookie.getValue();
}
}
}
// 返回默认值:
return "en";
}
2、URL 重写(传参):
URL 可以在后面附加参数,和服务器的请求一起发送。当服务器程序调用request.getSession()代码时,其会先看request.getCookies()方法中有没有名为JSESSIONID的cookie带过来,如果没有,就看URL有没有被重写(即附带JSESSIONID),如果有,则从服务器中找key为JSESSIONID的session对象,如果都没有,则创建一个新的session。如果用户禁用了cookie,则只能通过URL重写方式实现会话跟踪!
在Servlet中实现URL重写:
//客户端在访问本Servlet后,会返回主页面
package edu.session;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class EncodeURL extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
request.getSession(); //创建session
//调用response的encodeURL方法,将自动将JSESSION追加到url后面,如:url;jsessionid=BD111FFC653497E81B702A29B3AC6FE4
String buyurl = response.encodeURL("/CookieAndSession/servlet/buy");
String payurl = response.encodeURL("/CookieAndSession/servlet/pay");
out.print("<a href='"+buyurl+"'>购买</a><br/>");
out.print("<a href='"+payurl+"'>结账</a><br/>");
}
}
3、隐藏表单域(form控件):
hidden控件,适合步需要大量数据存储的会话应用。
<input type="hidden">
隐藏域和URL重写有着共同的优点:
它们在Cookie被禁用或者根本不支持的情况下依旧能够工作。
缺点:
所有页面必须是表单提交之后的结果,还有涉及许多冗长的处理工作。
4、Session技术(服务器):键值对
代表与用于某个web客户端的一个用户体验相关的对象和属性。是最常用的一种会话跟踪技术
Session代表服务器与浏览器的一次会话过程,这个过程是连续的,也可以时断时续的。在Servlet中,session指的是HttpSession类的对象。
//Session常见方法
package com.xx.session;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Servlet implementation class GetSessionServelt
*/
public class GetSessionServelt extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public GetSessionServelt() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// HttpSession getSession()
// 返回关于该请求的当前会话。或者若该请求没有会话则就创建一个。
// HttpSession getSession(boolean create)
// 返回有关本请求的当前HttpSession,或者若该请求没有会话,且“创建”属性为真,则就创建一个。
// 1、拿到session对象
HttpSession session = request.getSession(true);
// 2.获取session的id
String id = session.getId();
response.getWriter().println("session_id:" + id);
// 3.判断session的新旧
boolean isNew = session.isNew();
response.getWriter().println("isNew:" + isNew);
// 4.获取会话创建的时间
long time = session.getCreationTime();
response.getWriter().println("createtime=" + time);
// 5.设置最大有效时间,以秒为单位
session.setMaxInactiveInterval(200);
// 6.session失效
// session.invalidate();
// 7.浏览器最后一次请求的时间
long lastAccessedTime = session.getLastAccessedTime();
response.getWriter().println("lastAccessedTime=" + lastAccessedTime);
// 8.session也是域对象
session.setAttribute("name", "aSession");
String name = (String) session.getAttribute("name");
session.removeAttribute("name");
}
/**
* @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);
}
}
开发中的应用
1、显示当前登录用户信息
思路:登录成功后,将登陆信息 存储到用户的session对象中
步骤1:LoginAction中的成功分支中(跳转前),往session中存数据:
// 往session中,设置命名属性
HttpSession session = request.getSession(true);
session.setAttribute("LoginUser", user);
步骤2:在需要显示当前用户信息的界面中,从session中取数据
// 从session中,获取命名属性
HttpSession session = request.getSession(true);
Object obj = session.getAttribute("LoginUser");
User u = (User)obj;
out.println("当前用户:"+u.getUsername()+" 退出<hr/>");
2、强制用户登录(通过拦截器实现)
思路:获取并判断session对象中是否存储了用户的登陆信息,如果保存了则说明该用户已经登陆过;否则:说明该用户没有登陆,就强制回到登录页面。
//通过注解配置该拦截器的拦截路径规则
@WebFilter("/*") //拦截所有请求
//@WebFilter("*.do")//拦截后缀名为.do的所有请求
public class MyFilter implements Filter{
//检查登录状态
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//检查登录状态
//1.请求路径
HttpServletRequest httpRequest=(HttpServletRequest)request;
String requestURI = httpRequest.getRequestURI();
System.out.println("请求路径:"+requestURI);
//2.如果请求路跟登录无关,则检查登录状态
if(!requestURI.endsWith("login.jsp") && !requestURI.endsWith("login.do")){
//检查登录状态
HttpSession session = httpRequest.getSession();
boolean isLogin=session.getAttribute("isLogin") == null?false:(boolean)session.getAttribute("isLogin");
if(isLogin) {
//向后继续执行(执行过滤器链FilterChain中的下一个执行节点(Filter\Servlet\JSP))
chain.doFilter(httpRequest, response);
}else {
//跳转至登录页面
HttpServletResponse httpResponse=(HttpServletResponse)response;
httpResponse.sendRedirect("login.jsp");
}
}
//向后执行(执行过滤器链 FilterChain中的下一个执行节点(Filter/Server/Jsp))
chain.doFilter(httpRequest, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
3、安全退出
思路:删除session中存储的用户登陆信息,并且让session失效。
// 从session中,移除命名属性
HttpSession session = request.getSession(true);
session.removeAttribute("LoginUser");
// 让session失效
session.invalidate();
// 跳转到login
response.sendRedirect("/xxx/login.html");
如果文章对你有帮助,请留下点赞和关注,谢谢!