创建Session一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用HttpServletRequest.getSession(true)这样的语句时才被创建。如果JSP没有显示的使用 <%@page session="false"%>
关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);
这也是JSP中隐含的session对象的来历。 由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。
在服务器系统中会为每一个会话(浏览器)维护一个Session。不同的会话,对应不同的Session,那么系统是如何识别各个Session对象的?即如何在一个会话过程中,一直使用一个Session对象。
有四个步骤:
服务器当前应用的Session是以Map管理的,这个Map称为Session列表。该Map的Key为一个长度为32位长度的随机串,这个随机串为JSESSIONID,Value则为HttpSession对象的引用
Key | Value |
---|---|
Q12GH6YUIO8764BDK84NZUI3NR67S5S6 | HttpSession的引用1 |
1B4HDI387DYEN7XIW72MLZ72NE7AB3E4 | HttpSession的引用2 |
… | … |
当用户第一次提交请求时,服务端Servlet执行到request.getSession()方法后,会自动生成一个Map.Entry对象,Key为JSESSIONID,Value为新创建HttpSession对象
在再将Session信息写入Session 列表后,系统还会将“JSESSIONID”作为name,“32位长度的随机串”作为value,以Cookie的形式存放到响应报头中,并随着响应将此Cookie发送到客户端。对于这个响应报头实现的前提是你调用getSession方法。
客户端接收到这个Cookie后会将其存放到浏览器的缓存中,即只要客户端浏览器不关闭,浏览器缓存中Cookie就不会消失
当用户提交第二次请求时,会将这个缓存中的Cookie,伴随着请求的头部信息,一块发送到服务端
服务端从请求读取到客户端发送来的Cookie,并根据Cookie中JSESSIONID的值,从Map中查找相应的Key对应的Value,即Session对象。然后对该Session对象的域属性进行读写操作。
session失效后,但失效的Session并不为null。
下面是官方API文档解释
invalidate()
Invalidates this session then unbinds any objects bound to it.
还有一个使Session失效的方法是removeAttribute()
方法
在应用中,通过Session的工作原理,服务器之所以可以针对不同的会话找到不同的Session,是因为Cookie完成了会话的跟踪。但是,若客户端禁用Cookies后,服务器就无法维持同一个Session,因为无法从客户端获取并向服务端发送JSESSIONID。这是服务器就会认为是新的Session,就会有创建新的JSESSIONID。
Session依赖于Cookies存在,
在解决这个问题之前,要明确Session和Cookies分别在客户端和服务端都有存在!
默认情况下,当getSession()后,session就被被创建。session在创建时,服务器会通过Cookie返回session 的ID给浏览器,之后服务器根据浏览器Cookie里的session的ID来分辨不同用户。但是,这种方法返回的cookie是保存在浏览器的内存中,浏览器关闭后内存会被清理,所以在session在关闭浏览器后就失效了。虽然在服务器保存的session会在在有效期后才会被销毁,但是用户的cookie里没有session的ID,服务器就不能判断出当前用户是否是原先的那个用户。
解决方法:
我们可以创建一个新的CooKie,该Cookie的名字为JSESSIOID,path为WEB应用的虚拟路径,并设置setMaxAge()的毫秒值,让Cookie保存在客户端的硬盘中,这时即使多次对浏览器进行关开操作是不会清楚客户端硬盘文件的。所以,Cookie就不丢失了,SessionId也不会随浏览器关闭而丢失。
Cookie cookie = new Cookie("JSESSIONID",session.getId());
cookie.setPath(request.getContextPath()+"/");
cookie.setMaxAge(30*60);
response.addCookie(cookie);
由于cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为https://localhost/资源路径;jsessionid=32位字符串
另一种是作为查询字符串附加在URL后面,表现形式为https://localhost/资源路径?jsessionid=32位字符串
这两种方式对于用户来说是没有区别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。 为了在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id。
调用encodeRedirectURL()
方法重写URL
String uri = request.getContextPath()+"/otherServlet";
uri = response.encodeRedirectURL(uri);
response.sendRedirect(uri);
在超链接转发的时候禁用,禁用Cookies,SomeServlet向客户端发送来Cookie,客户端不可以存储到本地,当超链接到OtherServlet的时候,即使是同一个会话,也不可以访问Session的域内容。这里可以使用encodeURL()
方法对URL进行重写
response.setContentType("text/html;charset=utf-8");
PrintWrite out = response.getWrite();
String uri = "otherServlet";
uri = response.encodeURL(uri);
out.print("跳转到OtherServlet");