Session 管理是Web应用开发中的一个重要的内容,其实每天我们浏览网站,网站的后台都是通过这门技术来记录我们的浏览状态,最典型的就是登录,每次你在网站上登录一次,当跳转到该网站的任何其他页面都不会再次要求你登录,这就是使用了Session管理技术。那么问题来了我们为什么需要这门技术?这是因为Http协议是无状态的,什么是无状态?简单来说就是Web服务器不能区分请求是否来自一个人,也就是说每个用户发送的http请求是独立的,彼此之间没有联系的,所以说服务器不知道请求是来自初次用户还是之前已经访问过的用户。但是现实情况又有这种需要记住是谁发的请求的需求,所以说我们就要提出一些具体的技术来解决这个问题,就是对我们的session进行追踪。
对Session 进行追踪主要有4种技术:
一、 网址重写
网址重写怎么做?是通过在将要访问的URL后加上一个类似查询字符串的标识,以此来区分是是哪个请求。比如我们要访问的URL是这样的:
- http://localhost:8080/JavaServlet/testServlet
- <a href='?name=lmy'>点我</a>
- http://localhost:8080/JavaServlet/testServlet?name=lmy
- 不安全:最明显的就是全是查询字符串明文,简直是裸奔嘛!!
- 数量太多:点击的每个链接都要加上href后的查询字符串,想想某宝的一个页面,多到数不清!如果想跨越多个页面时处理起来就比较麻烦了。
- 静态页面麻烦:要实现加入链接必须在服务器端实现,也就是对动态页面支持比较好,但是静态页面就比较费劲了!
- 字符限制:有的浏览器对URL的字符数量是有限制的;
二、 隐藏域
利用隐藏域来保持状态类似于网址重写,但是不会加入到URL后面,而是将它们放到HTML表单的隐藏域中,当提交表单时隐藏域中的值也会传送到服务器。
- <input type='hidden' name='name' value='lmy'></input>
- 信息不用直接卸写在URL后面了,也就说可以穿衣服了(虽然有点少);
- 信息的字符数量不受限制;
没有解决的问题:
- 数量太多:每个页面都得写一个隐藏域,所以在跨越多个页面传递信息时也是不方便的;
- 既然是使用隐藏域,那么必须有表单,这就给它的使用带来了很大限制;
三、 Cookie
鉴于以上两种技术没有很好的解决保持Http连接的问题,cookie出现了,而且Http协议也对cookie进行支持,它是嵌入到Http请求头的,所以它的传输由http协议处理。
那么cookie是什么?它只是在Web服务器和客户端之间来回传递的一小块信息。在客户端初次访问Web服务器时,由web服务器在Http响应消息头set-Cookie中将cookie发送给客户端,一旦客户端(浏览器)保存了某个cookie,那么以后每次访问该web服务器时都会在http请求头Cookie字段中将该信息回传给服务器,它的特点:
- 一个cookie只能标识一种信息,该信息用一对键值对表示;
- 一个web服务器可以给客户端(浏览器)发送多个Cookie,一个客户端(浏览器)也可以存储多个服务器提供的Cookie;
- 浏览器一般只允许存放300个cookie,每个服务器最多存放20个cookie,每个cookie的大小限制为4kB;
- 该功能是可以被客户端(浏览器)禁用的;
- 不可跨域名性:即哪个服务器返回的cookie,那么这个cookie就只能用于哪个服务器,即www.google.com返回的cookie不能被www.baidu.com读取;
创建一个cookie如下:
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- Cookie cookie = new Cookie("name", "lmy86263");
- cookie.setComment("auth purpose");
- cookie.setMaxAge(3600);
- resp.addCookie(cookie);
- resp.getWriter().write("cookie return");
- }
注意默认情况下,该cookie是一个session级别的cookie,它
存储在客户端程序(浏览器)的内存中,当客户端程序关闭时则被删除,如果要
保存在磁盘上则要设置maxAge属性,这样cookie就以文本文件的形式存储在本地了;还要注意实际情况中的cookie的name和value都是加密的,让你猜不出是什么意思。
获取请求回传的cookie:
- Cookie[] cookies = req.getCookies();
- if(cookies != null){
- for(Cookie cookie: cookies){
- //...
- }
- }
真是蛋疼,只能返回一个cookie数组,不能根据键返回一个值。
删除cookie:
- Cookie cookie = new Cookie("name", "");
- cookie.setMaxAge(0);
- resp.addCookie(cookie);
为了在服务端删除一个cookie,也是挺费事的,得重新生成一个并且把它的生存时间设置为0才能在服务端删除cookie。
cookie的作用范围:
cookie的作用范围是什么意思?简单来说就是cookie在服务器上能够被识别的页面有哪些,因为服务器返回的cookie只能作用当前的目录和它的子目录,不能作用当前目录的父目录。想想就觉得有问题,如果你在某宝的开始页面没有登录,而是先浏览着,后来发现中意的东西要登录付款,结果登陆后不能发现访问之前的页面还需要再登录一次,有没有很气愤。如果想让cookie作用于整个web服务提供的页面,就要使用setPath()这个API:
- cookie.setPath(request.getContextPath());
这样就使得该cookie对web应用的所有的目录都是有效的。
下面看一下cookie的传递过程:
下面看一下cookie的传递过程:
第一次访问某个web应用:
第二次再访问该服务器时:
四、 HttpSession
1、 初识HttpSesion
在追踪Session的所有的技术中,HttpSession最为强大,不向Cookie一样将与服务器保持联系的信息保存在本地磁盘上,Session的信息是保存在服务器的内存上的。用它来保持与客户端的联系是通过如下的方式完成的:当客户端发送过来一个要求创建session的请求时,服务器首先检查该发送过来的请求是否有session标识符,也就是sessionId,如果携带有该标识符,则服务器程序根据该id查找到对应的session对象;如果没有携带sessionId,则服务器认为该客户端之前没有发送过请求(也可能是客户端长时间不活动导致session过期,没当做垃圾回收了)并为它生成一个新的session 对象,同时将该对象的标识符sessionId作为相应的一部分发送给客户端,这部分是通过cookie技术实现的,也即服务器将sessionId作为一个cookie返回给客户端,加在Http的请求头的Set-Cookie部分。
在服务器上设置session,由于是第一次访问之前没有设置过session:
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- HttpSession session = req.getSession();
- resp.getWriter().write("session return ");
- }
只是使用如下代码即可:
request.getSession()会检查是否存在sessionId,如果没有则会重新创建一个,并随响应一起返回,如果有的话则返回该Id对应的session对象,还有一个类似的API:
- HttpSession session = req.getSession();
- HttpSession session = req.getSession(true);
详细的过程看如下图:
上图是发送一个没有sessionId的请求,结果在响应中发现在Set-cookie中返回一个名为JSESSIONID的cookie。
上图是在向同一个网站发送第二次请求时的请求头与响应头,可以看出服务器已经检测出存在sessionId,所以没有新生成,并且可以看到在请求头中的Cookie中的JSESSIONID的值和第一次发送回来JSESSIONID的值一样。在以后的每一个发送的请求中都会携带有这样的JSESSIONID,除非session过期被当成垃圾回收了,这时候就要重新建立一个。注意在session中使用的cookie是存在于内存中的,称为session cookie,如果客户端程序关闭则该cookie消失,其实我们可以将该session cookie持久化到内存,因为它是通过cookie来存储的,所以我们通过cookie来进行持久化,如下:
- Cookie cookie = new Cookie("JSESSIONID", session.getId());
- cookie.setMaxAge(60);
- resp.addCookie(cookie);
2、 Session的生命周期
前面我们已经说了,session在服务上是保存在内存中的,如果一个网站的访问人数很多,那就需要建立很多的session,这对服务器造成的负担很大,但是偏偏有些人访问一次后之后就不再访问了,但是我们还保存有他们留下来的session,这就浪费了服务器的资源了,我我们通过设置session的生命周期来管理每个session。
session的生命周期无非包含两个问题:
- 这个session什么时候产生?
之前说过这个问题,在客户端(浏览器)第一次访问web应用程序的资源的时候,如果要求使用session,会创建session对象,并返回sessionId。
- 这个session什么时候死亡?
- 当调用session的invalidate()方法时;
- 当在服务器上的web应用程序停止运行时;
- 当不活动时间超出session的过期时间;
3、 Session的过期时间
session的过期时间可以通过三种方式来设置:
- 通过API来设置,单位s:
- session.setMaxInactiveInterval(3600);
- 通过web.xml进行配置,单位min:
- <session-config>
- <session-timeout>60</session-timeout>
- </session-config>
- 通过配置所有的web.xml文件的父文件来改变,如果默认在自己的工程中没有设置session的过期时间,则会直接使用servlet容器提供的默认的过期时间,该文件在tomcat的conf;
- <!-- ==================== Default Session Configuration ================= -->
- <!-- You can set the default session timeout (in minutes) for all newly -->
- <!-- created sessions by modifying the value below. -->
- <session-config>
- <session-timeout>30</session-timeout>
- </session-config>
4、 重写URL
通过cookie来传回sessionId时可能会遇到一个问题,就是客户端(浏览器)禁止使用cookie,虽然普通使用者都不会这样做,但不排除一些专业人士,如果这样我们就没有办法传回sessionId,这时候可以通过重写URL的方式来实现,URL地址重写的原理是将该用户Session的id信息重写到URL地址中。HttpServletResponse中的encodeURL()来编码要返回的URL,它会自动判断客户端(浏览器)是否支持cookie然后决定返回的URL是什么类型的。
- 如果浏览器支持cookie,则返回正常的URL;
- 如果浏览器不支持cookie,则会返回URL+";jsessionid=******"类型的URL
- <a href="index.jsp;jsessionid=0CCD096E7F8D97B0BE608AFDC3E1931E">点我</a>
5、 session API
HttpSession提供了很多的API,在这其中比较重要的有两个getAttribute()和setAttribute(),使用这两个方法可以保存一些简短的信息,比如访问计数:
- HttpSession session = request.getSession();
- //输出sessionID
- System.out.println(session.getId());
- //获取绑定的计数器
- Integer count=(Integer) session.getAttribute("count");
- if(count==null){
- count=1;
- }else{
- count++;
- }
- //在session中绑定计数器
- session.setAttribute("count", count);