本篇供个人学习使用,有问题欢迎讨论
其中有部分Cookie内容,不清楚的可以参考我的另一篇博文Cookie的使用
HttpSession
Session,即会话,是 Web 开发中的一种会话状态跟踪技术。当然,Cookie也是一种会话跟踪技术。不同的是 Cookie是将会话状态保存在了客户端,而Session则是将会话状态保存在了服务器端。
那么,到底什么是 “ 会话 ” ? 当用户打开浏览器,从发出第一次请求开始, 一直到最后关闭浏览器,就表示一次会话的完成。
Session并不是 JavaWeb 开发所特有的,而是整个Web开发中所使用的技术。在 JavaWeb开发中,Session是以 javax.servlet.http.HttpSession的接口对象的形式出现的。
一、Session的访问
1、Session 对象的创建
若要对Session进行操作,则可以通过HttpServletRequest的getSession()方法获取。该方法具有两个重载的方法。
(1)public HttpSession getSession(boolean create)
该方法用于创建 Session。
- 若参数 create 为 true,则表示若当前没有 Session,则新建一个Session
- 若当前存在 Session,则使用当前的 Session
- 若参数 create 为 false 表示若当前没有Session,则直接返回 null
(2)public HttpSession getSession()
该方法用于创建 Session。
- 相当于 getBession(true),即没有 Session 则创建新的 Session。
2、何时使用getSession(true),即 getSession)。何时使用 getSesson(false)呢?
一般情况下, 若要向 Session 中存放数据,则使用 getSession(true),即getSession()。意义为:若当前存在 Session,则使用当前的 Session;若当前不存在 Session,则创建一个新的Session。因为存放数据是必须要有 Session的。
若要从 Session 中获取数据,则一般使用 getSession(false)。意义为:若当前存在 Session,则从中获取数据;若当前根本就没有 Session,那就更不可能存在 Session中的数据了。无需创建一个新的 Session。再从新的 Session 中获取数据,因为新创建的 Session 中是不可能有数据的。
3、对 Session 域属性空间的操作
Session是一个专门用于存放数据的集合,我们一般称这个用于存放数据的内存空间为域属性空间,简称域。HttpSession 中具有三个方法,是专用于对该域属性空间中数据进行写、读操作的。
(1)该方法用于向 Session 的域属性空间中放入指定名称、指定值的域属性
public void setAttribute(String name, Object value)
(2)该方法用于从 Session 的域属性空间中读取指定名称的域属性值
public Object getAttribute(String name)
(3)该方法用于从 Session 的域属性空间中删除指定名称的域属性
public void removeAttribute(String name)
4、Session 对象的使用
(1)hhh.html
<form action="/myWeb/one.do">
客户:<input type="text" name="username" /><br>
<input type="submit" value="点餐" />
</form>
(2)OneServlet
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取用户提交的参数
String name = request.getParameter("username");
//将参数放入request域中
request.setAttribute("user",name);
//获取Session对象
HttpSession session = request.getSession();
//向Session域中写入属性
session.setAttribute("username",name);
//在OneServlet页面上进行显示
PrintWriter out = response.getWriter();
out.println("OneServlet user :" + name);
out.println("OneServlet Session :" + session);
}
(3)TwoServlet
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//从request域中读取user属性
String user = request.getParameter("user");
//获取Session
HttpSession session = request.getSession(false);
//从Session中读取指定属性
String username = null;
if (session != null){
username = (String) session.getAttribute("username");
}
//在TwoServlet页面上进行显示
PrintWriter out = response.getWriter();
out.println("TwoServlet user :" + user);
out.println("TwoServlet username :" + username);
out.println("TwoServlet session :" + session);
}
二、Session的工作原理(重点)
在服务器中系统会为每个会话维护一个Session。不同的会话,对应不同的Session。那么,系统是如何识别各个Session对象的?即是如何做到在同一会话过程中,一直使用的是同一个 Session对象呢?
1、写入 Session 列表
其实当前应用中的 Session 是以 Map 的形式进行管理的,这个Map称为Session 列表。该 Map的 key 为一个32位长度的随机串,这个随机串称为 JSessionID,value 则为 Session对象的引用。
当用户第一次提交请求时, 服务端 Servlet 中执行到 request.getSession() 方法后,会自动生成一个 Map.Entry 对象,key 为一个根据某种算法新生成的 JSessionID,value则为新创建的 HttpSession对象。
2、服务器生成并发送Cookie
在将 Session 信息写入 Session 列表后,系统还会自动将 “JSESSIONID” 作为 name,这个32位长度的随机串作为 value,以 Cookie 的形式存放到【响应头】中,并随着响应,将该 Cookie发送到客户端。
3、客户端接收并发送Cookie
客户端接收到这个Cookie后会将其存放到浏览器的缓存中。即只要客户端浏览器不关闭,浏览器缓存中的 Cookie就不会消失。
当用户提交第二次请求时,会将缓存中的这个Cookie,伴随着请求的头部信息,一块发送到服务端。
4、从Session列表中查找
服务端从请求中读取到客户端发送来的Cookie,并根据Cookie的 JSSESSIONID 的值,从Map中查找相应key 所对应的value,即Session对象。然后,对该Session对象的域属性进行读、写操作。
三、Session的失效
Web 开发中引入的Session超时的概念,Session的失效就是指Session的超时。若某个Session在指定的时间范围内一直未被访问,那么Session将超时,即将失效。在 web.xml 中可以通过 <session-config> 标签设置 Session的超时时间,单位为分钟。默认Session的超时时间为 30分钟。需要再次强调的是,这个时间并不是从 Session被创建开始计时的生命周期时长,而是从最后一次被访问开始计时,在指定的时长内一直未被访问的时长。
<!--设置Session的失效时间-->
<session-config>
<session-timeout>120</session-timeout> //失效时长为120分钟
</session-config>
若未到超时时限,也可通过代码提前使Session失效。HttpSession中的方法 invalide(),使得Session失效。
session.invalidate();
System.out.println("session: " + session);
以上代码表示使 Session失效,但失效的Session值并不为 null。如图所示:
四、Cookie禁用后使用Session进行会话跟踪
从前面 Session 的工作原理可知,服务器之所以可以针对不同的会话找到不同的 Session,是因为 Cookie 完成了会话的跟踪。但是,若客户端浏览器将 Cookie 禁用,那么服务器还怎样保证同一会话使用的是同一个Session呢?
若客户端浏览器禁用了。Cookie 会发现向服务器所提交的每一次请求,服务器在给出的响应中都会包含名称为 JESSIONID 的 Cookie,只不过这个 Cookie 的值每一次都不同。 也就是说,只要客户端浏览器所提交的请求中没有包含 JESSIONID,服务器就会认为这是一次新的会话的开始,就会为其生成一个 Map.Entry 对象,key 为新的32位长度的随机串,value 为新创建的 Session 会话用。这样的话,也就无法实现会话跟踪了。
1、禁用Cookie(谷歌浏览器)
(1)【设置】-------> 【高级设置】
(2)【网站设置】-------> 【权限】
(3)即可在Cookie中设置了
2、重定向的URL重写(解决Cookie禁用后,Session跟踪问题)
(1)当仅使用普通重定向时,跳转到 TwoServlet 时,没有相应的 JESSIONID 的值
response.sendRedirect(request.getContextPath() + "/two.do");
(2)对重定向使用 encodeRedirectURL 后,即可在 Cookie 禁用的状态下依然跟踪 Session
String uri = request.getContextPath() + "/two.do";
uri = response.encodeRedirectURL(uri);
response.sendRedirect(uri);
(3)即使打开Cookie后,依然能使用重定向的跳转。
3、超链接的URL重写(**解决Cookie禁用后,非重定向时的Session跟踪问题)
HttpServletResponse具有一个方法 encodeURL,可以完成类似对超链接这样的非重定向页面跳转的 URL 的重写,即在其路径后会自动添加 jsessionid。
(1)当只使用超链接跳转时,跳转到 TwoServlet 时,没有相应的 JESSIONID 的值
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<a href='two.do'>跳转<a>到TwoServlet");
(2)对超链接使用 encodeURL 后,即可在 Cookie 禁用的状态下依然跟踪 Session
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
String uri = "two.do";
uri = response.encodeURL(uri);
out.println("<a href='"+ uri +"'>跳转<a>到TwoServlet");
注意:当每次禁用Cookie后跟踪Session时,jsessionid都会暴露在地址栏中,存在危险。因此,能不禁用Cookie时尽量别禁用!!!
地址栏中的 jsessionid 前使用的是“ ;(分号)”,不是冒号 。
五、域属性空间范围对比
在 JavaWeb 编程的 API 中,存在三个可以存放域属性的空间范围对象,这三个对象中所存储的域属性作用范围,由大到小分别为:
ServletContext:即 application,置入其中的域属性是整个应用范围的,可以完成跨会话共享数据
HttpSession:置入其中的域属性是会话范围的,可以完成跨请求共享数据
HttpServletRequest:置入其中的域属性是请求范围的,可以完成跨 Servlet 共享数据。但这些 Servlet 必须在同一请求中。
对于这三个域属性空间对象的使用原则是,在可以保证功能需求的前提下,优先使用小范围的。这样不仅可以节省服务器内存,还可以保证数据的安全性。
六、购物车的简单实现
1、hhh.html(商品展示页面)
<center>
<h1>商品展示页面</h1>
<table border="2">
<tr>
<td>面包</td>
<td>15.00</td>
<td><a href="/myWeb/one.do?name=面包">放入购物车</a></td>
</tr>
<tr>
<td>啤酒</td>
<td>10.00</td>
<td><a href="/myWeb/one.do?name=啤酒">放入购物车</a></td>
</tr>
<tr>
<td>烤肠</td>
<td>5.00</td>
<td><a href="/myWeb/one.do?name=烤肠">放入购物车</a></td>
</tr>
</table>
</center>
2、根据商品名称加入购物车
//获取浏览器推送过来的名称
String name = request.getParameter("name");
//向Tomcat索要当前浏览器在服务端的Session对象
HttpSession session = request.getSession();
//将用户选择商品保存到当前用户Session
Integer nums = (Integer) session.getAttribute(name);
if (nums == null){
session.setAttribute(name,1);
} else {
session.setAttribute(name,nums+1);
}
当商品数量为空时,如把烤肠加入到购物车:
此时已加入到购物车中,商品数量为1,返回商品展示页面,再次对烤肠进行操作,加入到购物车:
如何调出以上界面?
先对程序加入断点,运行到最后一行,如下图所示:
之后把鼠标放到任意 session 上,点击 session,过一会出现如下图所示:
此时点击+号即可出现对 session 的详细页面: