会话跟踪技术
- 什么是会话?
会话可简单理解为:打开浏览器 -> 访问一些服务器内容 -> 关闭浏览器,整个过程称之为一个会话。- 会话过程中要解决的问题?
HTTP 是一种“无状态”协议,每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,服务器要想办法为每个用户保存这些数据:
如:用户登录场景,当不同的用户登录系统后,我们如何在主页面显示不同的用户名?- 会话跟踪技术是一种在客户端与服务器间保持HTTP状态的解决方案,管理浏览器客户端和服务器端之间会话过程中产生的会话数据。从开发角度考虑,是使上一次请求所传递的数据能够维持状态到下一次请求,并且辨认出是否相同的客户端所发送出来的。
- 会话跟踪技术的解决方案主要分为以下几种:
Cookie技术
Session技术
URL重写技术
隐藏表单域技术
一、Cookie
- Cookie技术是一种在客户端保持会话跟踪的解决方案,会话数据保存在客户端浏览器。
- Cookie在用户第一次访问服务器时,由服务器通过响应头的方式发送给客户端浏览器;当用户再次向服务器发送请求时会附带上这些文本信息。
在使用Cookie时,要保证客户端浏览器接受Cookie。
- 存储Cookie会话数据
1.构造Cookie对象
- Cookie(java.lang.String name, java.lang.String value);
2.设置cookie
- void setPath(java.lang.String uri):设置cookie的有效访问路径。
默认cookie只能由创建它的应用获得,设置cookie.setPath(“/”),则可在同一应用服务器内的所有应用共享访问。- void setMaxAge(int expiry) : 设置cookie的有效时间
该Cookie失效时间,单位秒。如果为正数,则该Cookie在expiry秒之后失效。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1- void setValue(java.lang.String newValue) :设置cookie的值
Cookie的值不能设置成非ASSIC字符,如果要使用中文可以通过URLEncoder将其编码,否则会出异常。
Cookie大小和数量的限制,不同的浏览器有所区别,一般每个域名最多可存储
50个cookie,Cookie总的的大小限制为4KB。
3.将Cookie对象响应给客户端浏览器,存储在客户端。
- void response.addCookie(Cookie cookie) : 发送cookie
@WebServlet("/CookieStorageServlet") public class CookieStorageServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie ck1 = new Cookie("name",URLEncoder.encode("张三","UTF-8")); Cookie ck2 = new Cookie("jSessionId", "10001"); Cookie ck3 = new Cookie("email","[email protected]"); Cookie ck4 = new Cookie("status","1"); ck1.setMaxAge(-1);//默认值 ck2.setMaxAge(0);//删除cookie ck3.setMaxAge(24*60*60);//设置1天后失效 ck3.setPath("/ch03-cookie/shopping"); ck4.setMaxAge(10);//10秒后失效 response.addCookie(ck1); response.addCookie(ck2); response.addCookie(ck3); response.addCookie(ck4); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
- 获取Cookie会话数据
存储在客户端的Cookie,通过HttpServletRequest对象的getCookies()方法获取,该方法返回所访问网站的所有Cookie的对象数组,遍历该数组可以获得各个Cookie对象。
@WebServlet("/CookieGetServlet") public class CookieGetServlet extends HttpServlet { private static final long serialVersionUID = 1L; 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(); Cookie[] cookies = request.getCookies(); for(Cookie c:cookies) { if("name".equals(c.getName())) { out.println("
" + URLDecoder.decode(c.getValue(),"UTF-8") + "
"); }else { out.println("
" + c.getValue() + "
"); } out.println("
" + c.getName() + "
"); out.println("=========================="); } out.close(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
- Cookie的局限性
- Cookie可能被禁用,当用户非常注重个人隐私保护时,很可能会禁用浏览器的Cookie功能;
- Cookie可能被删除,因为每个Cookie都是硬盘上的一个文件,因此很有可能被用户删除;
- Cookie的大小和个数受限,不同浏览器有所区别,Cookie保存的数据不能超过4095个字节,50个/每个域名;
- Cookie安全性不够高,所有的Cookie都是以纯文本的形式记录于文件中,因此如果要保存用户名密码等信息时,最好事先经过加密处理。
二、Session
- Session是指使用HttpSession对象实现会话跟踪的技术,是一种在服务器端保持会话跟踪的解决方案。
- HttpSession对象会在用户第一次访问服务器时由容器创建(注意只有访问JSP、Servlet等程序时才会创建,只访问HTML、IMAGE等静态资源并不会创建),当用户调用其失效方法(invalidate()方法)或超过其最大不活动时间时会失效。在此期间,用户与服务器之间的多次请求都属于同一个会话。
- Session和Cookie的主要区别在于:
Cookie是把用户的数据写给用户的浏览器。
Session技术把用户的数据写到用户独占的session中。
- Session工作原理
服务器在创建会话对象时,会为其分配一个唯一的会话标识——SessionId,以“JSESSIONID”的属性名保存在客户端Cookie中,在用户随后的请求中,服务器通过读取Cookie中的JSESSIONID属性值来识别不同的用户,从而实现对每个用户的会话跟踪。
- 获取HttpSession对象
@WebServlet("/CreateSessionServlet")
public class CreateSessionServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//获取HttpSession对象标识sessionid
out.println("" + session.getId() + "
");
//获取HttpSession对象产生的时间,单位是毫秒
out.println("" + session.getCreationTime() + "
");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
- Session存取数据
- 存储会话域属性
session.setAttribute("username"," neuedu ");- 会话域中获取属性值
String uname = (String)session.getAttribute("username");- 会话域中删除属性
session.removeAttribute("username");
- 例:当用户当用户登录成功后,将用户信息存储到session中,并重定向到主页面显示用户信息。
Insert title here
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取session对象
HttpSession session = request.getSession();
//模拟登陆成功
String userName = request.getParameter("userName");
//向session中存储用户名
session.setAttribute("userName",userName);
response.sendRedirect("index");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
@WebServlet("/index")
public class IndexServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//获取session对象
HttpSession session = request.getSession();
String userName = (String)session.getAttribute("userName");
//判断是否登录成功:
if(userName == null || "".equals(userName)) {
//没有登录成功,跳转到login.html
response.sendRedirect("login.html");
return;
}
//设置session的非活动时间
session.setMaxInactiveInterval(10);
out.println(" 欢迎你:" + userName + "
");
out.println("退出登录");
out.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
- Session生命周期
- Session失效时间
session具有一定声生命周期,如果session超过会话的最大不活动时间,会话自动失效,会话的最大不活动时间指会话超过此时间段不进行任何操作;- 设置Session的失效时间
方法1:在工程的web.xml中配置session的生命周期,单位为分钟;
15
方法2:在程序硬编码设置
session.setMaxInactiveInterval(30 * 60);//设置单位为秒,设置为-1永不过期;
方法3:在Tomcat安装目录下conf/web.xml中配置(Web容器级别)
15 - 手动销毁Session
可以通过调用invalidate()方法立即清除会话对象及其所有会话域属性,同时响应客户端浏览器清除Cookie中的JSESSIONID
- 在实际应用中,此方法多用来实现系统的“安全退出”功能。
@WebServlet("/logout")
public class LogoutSerlvet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//手动销毁sessoin
request.getSession().invalidate();
response.sendRedirect("login.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);
}
}
三、其它会话技术
- URL重写技术
- URL重写技术是实现动态网站会话跟踪的重要保障。在实际应用中,当客户端浏览器禁用Cookie后的session处理,当不能确定客户端浏览器是否支持Cookie的情况下,使用URL重写技术可以对请求的URL地址追加会话标识,从而实现用户的会话跟踪功能。
- 例如,对于如下格式的请求地址:
http://localhost:8080/Demo/EncodeURLServlet
经过URL重写后,地址格式变为:
http://localhost:8080/Demo/EncodeURLServlet;jsessionid=24666BB458B4E0A68068CC49A97FC4A9
其中:“jsessionid”即为追加的会话标识,服务器即通过它来识别跟踪某个用户的访问。
- URL重写实现方法
@WebServlet("/UrlRewriterServlet") public class UrlRewriterServlet extends HttpServlet { private static final long serialVersionUID = 1L; 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(); // 两个请求地址进行URL重写 String link1 = response.encodeURL("UrlRewriterResultServlet"); String link2 = response.encodeURL("UrlRedirectServlet"); // 使用超链接形式对URL重写地址进行请求 out.println(""); out.println(""); } protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { doGet(request, response); } }
@WebServlet("/UrlRewriterResultServlet") public class UrlRewriterResultServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { java.io.PrintWriter out = response.getWriter(); // 获取经URL重写传递来的会话标识值 String sessionId = request.getSession().getId(); out.println(sessionId); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
@WebServlet("/UrlRedirectServlet") public class UrlRedirectServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub String url = response.encodeRedirectURL("UrlRewriterResultServlet"); response.sendRedirect(url); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
- URL重写技术应用注意事项:
1.如果应用需要使用URL重写,那么必须对应用的所有请求(包括所有的超链接、表单的action属性值和重定向地址)都进行重写,从而将jsessionid维持下来;
2.由于浏览器对URL地址长度的限制,特别是在对含有查询参数的GET请求进行URL重写时,需要注意其总长度;
3.由于静态页面不能进行会话标识的传递,因此所有的URL地址都必须为动态请求地址。
- 隐藏表单域
- 利用Form表单的隐藏表单域,在用户无法从页面显示看到隐藏标识的情况下,将标识随请求一起传送给服务器处理,从而实现会话的跟踪。
- 在Form表单中定义隐藏域
- 在服务器端通过HttpServletRequest对象获取隐藏域的值
String flag = request.getParameter("userID");