5.4.4 通过Cookie跟踪Session

优质
小牛编辑
142浏览
2023-12-01

由于Session是服务端对象,浏览器要想使用某个服务端的Session对象,就必须在请求消息中包含该Session对象的SessionID。该SessionID一般被放到HTTP请求消息头的Cookie字段中,Cookie名称是JSESSIONID。下面的例子揭示了通过Cookie来跟踪Session的底层技术,读者可以从该例子中充分了解Cookie和Session的关系。

例子 : 通过Cookie跟踪Session

1. 实例说明

本例中的Servlet类的主要功能是将SessionID保存在临时Cookie或永久Cookie中。并且在不同的浏览器窗口中访问该Servlet。如果SessionID被保存在了永久Cookie中,当在新的浏览器窗口中,也同样可以通过SessionID访问HttpSession对象。而如果将SessionID保存在临时Cookie中,在新的浏览器窗口中,由于SessionID丢失,所以就无法找到并访问该HttpSession对象了。

2. 编写CookieSession类

package chapter5;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class CookieSession extends HttpServlet
{
    public void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        response.setContentType("text/html; charset=UTF-8");
        //  获得save请求参数
        Object save = request.getParameter("save");
        // 当HTTP请求消息头包含JSESSIONID时,查找并或得这个HttpSession对象
        // 否则,创建一个新的HttpSession对象
        HttpSession session = request.getSession();
        PrintWriter out = response.getWriter();
    // 已经新建立了一个Session对象
        if (session.isNew()) 
        {
            // 设置Session属性值,可以为其设置任何对象,在这里设置了一个字符串
            session.setAttribute("mySession", "Session信息");
// 设置Session有效时间,2小时
            session.setMaxInactiveInterval(2 * 60 * 60); 
            out.println("新会话已经建立<br/>");
             // 将SessionId持久化,也就是说,将这个SessionId保存在永久Cookie中
            if (save != null)
            {
                Cookie cookie = new Cookie("JSESSIONID", session.getId());
//  Cookie的有效期是2小时
                cookie.setMaxAge(2 * 60 * 60);  
                cookie.setPath(request.getContextPath());
                response.addCookie(cookie);
                session.setAttribute("mySession", "这个Session Id从Cookie获得");
                out.println("SessionId已经被保存在Cookie中:Session Id = " + session.getId());
            }
        }
    // 找到了HttpSession对象,输出Session属性值
        else 
        {
            out.println("会话属性值:" + session.getAttribute("mySession"));
     }
    }
}

从上面的代码可以看出,CookieSession通过一个save请求参数来决定是否将SessionID保存在永久Cookie中,如果存在save参数(save参数可以是任何值),则将SessionID使用JSESSIONID作为key值保存在了永久Cookie中,有效期为2小时。也就是说,在2小时之内,都可以使用找到并使用该HttpSession对象(除非手动删除本机的Cookie或关闭浏览器的Cookie功能)。

3. 配置CookieSession类

CookieSession类的配置代码如下:

<servlet>
    <servlet-name>CookieSession</servlet-name>
    <servlet-class>chapter5.CookieSession</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>CookieSession</servlet-name>
    <url-pattern>/CookieSession</url-pattern>
</servlet-mapping>

4. 在同一个浏览器窗口使用Session

在浏览器地址栏中输入如下的URL:

http://localhost:8080/demo/CookieSession

当首次访问上面的URL时,会在浏览器中显示“新会话已经建立”信息,当再次刷新页面时,就会显示“会话属性值:Session信息”,这说明已经找到Session了,并将Session属性的值取出。

5. 在新的浏览器窗口中使用Session

打开一个新的浏览器窗口,并输入上面的URL,仍然会显示“新会话已经建立”,表明在第4步建立的HttpSession对象在浏览器中的SessionID已经丢失,在这一步在服务端又重新建立了一个HttpSession对象。

6. 使用永久Cookie来保存SessionID

在浏览器地址栏中输入如下的URL:

http://localhost:8080/demo/CookieSession?save

在首次访问上面的URL时,浏览器中会显示如下的信息:

新会话已经建立

SessionId已经被保存在Cookie中:Session Id = 86311792B79503506337BDED51117B3D

打开一个新的浏览器窗口,再次输入上面的URL,就会显示“会话属性值:这个Session Id从Cookie获得”。这说明由于SessionID保存在永久Cookie中,在新的浏览器窗口中该SessionID仍然可用,因此,当再次访问上面的URL时,仍然可以找到HttpSession对象,所以在浏览器中会输出“会话属性值:这个Session Id从Cookie获得”信息。

7. 程序总结

在CookieSession类中通过save请求参数将SessionId保存在永久Cookie中。这样当关闭浏览器,甚至关闭计算机后,再打开计算机和浏览器时,仍然可以通过保存在硬盘上的SessionId找到相应的Session对象。

也许读者会有一个疑虑,将JSESSIONID作为永久Cookie保存后,在同一个浏览器窗口中,再次发出的HTTP请求头可能会有两个JSESSIONID值,虽然它们的值是一样的,但它们一个是临时Cookie,另一永久Cookie。不过这并不会对定位HttpSession对象有任何影响。服务端只取第一个JSESSIONID的值。

根据本例中的代码,可以将Session的原理总结如下:

一个Session实际上就是服务端的一个对象。我们可以想象,将服务端所有的Session对象放到一个Map中,那么可以找到某一个Session最容易的方法就是通过Key。而对于Session来说,这个唯一标识Session的Key叫做SessionId。从而可以得知,客户端要想找到属性自己的Session,就必须要知道这个Session的SessionId。

因此,在服务端第一次为某个客户端建立HttpSession对象时,会将这个HttpSession对象的SessionId通过HTTP响应消息头的Set-Cookie字段发送给客户端,格式如下:

Set-Cookie: JSESSIONID=4AD2607EF8A91F9EA8AC82B48851EED7; Path=/demo

其中JSESSIONID表示一个SessionId值,也就是一个临时Cookie的值(因为未设置Cookie有效时间,所以JSESSIONID是一个临时Cookie),而Path是由系统自动设置的,该值也可以通过setPath方法进行修改。一般path属性的值是当前Web应用程序的上下文路径。

当浏览器检测到HTTP响应消息头包含一个名为JSESSIONID的Cookie后,就会认为服务端已经建立了一个新的HttpSession对象。然后在同一个浏览器窗口再次访问Web应用程序时,就会在HTTP响应消息头中带上Cookie字段,格式如下:

Cookie: JSESSIONID=4AD2607EF8A91F9EA8AC82B48851EED7

当服务端检测到HTTP请求消息头的Cookie字段中有名为JSESSIONID的Cookie时,并在保存Session对象的Map对象中查找。如果找到了这个HttpSession对象,就会返回这个HttpSession对象。如果未找到和JSESSIONID对应的HttpSession对象(未找到的原因有很多,可能是客户端发送虚假的SessionId,也可能是Session过期被释放了),服务端有以下两种选择:

(1)建立一个新的HttpSession对象

(2)不创建HttpSession对象,getSession方法返回null。

从上面的描述可以看出,所谓Session,就相当于服务端的Map对象中的一个元素,然后通过在HTTP响应消息头和请求消息头中传递SessionId的方式将浏览器和Session联系起来。如果SessionId丢失,那么浏览器就无法找到这个Session,从浏览器的表现来看,有点象Session被删除了,但从内部实现来看,客户端是无法干预Session的创建和删除的。也就是说,客户端可能找不到某个Session,但该Session仍然存在于服务端(直到过期被Servlet引擎删除)。