Tomcat 服务器 Session的实现

2021-03-17 00:00:00 session tomcat 服务器

《Tomcat 服务器 Session的实现》

结合《HTTP权威指南》和《How Tomcat Works》话一话我所理解的Session以及Tomcat下的实现方式!

Session是什么?

在开始正式讨论之前,我们首先探讨一些原理性的问题,什么是session,它可以用来做什么!
我们每天都会浏览各种网站,不同的用户浏览网站的目的不同,在该网站上留下的历史也不同,为了提供个性化的服务,服务器就需要记录和识别用户!一个用户可以在不同地方,利用不同设备访问网站,服务器如何知道“我是谁”呢?很多人一下子就想到用户名和密码,对没错,这的确是一种方式。但是网站访问是一个连续的过程,每次访问都携带用户名和密码的做法提高了用户风险,更重要的是,如何解决非注册用户的问题呢,一些用户习惯每天打开新闻站点,但是他只是浏览信息,不想输入任何个人信息去注册网站,这样的用户如何提供个性化服务呢!

看看现实生活中政府是如何识别“我是谁”,并且提供差异服务的。我们每个人都有一张身份证,是政府机关发给我们的。身份证只是一个人的基本信息,例如姓名,身份证号,住址。上面并不会纪录你的学历,你的婚姻状况。当我需要这些信息的时候,我们提供身份证,相关的单位就会提供相关证明,例如在很多省份在购买房产的时候,需要未婚人士提供一个单身证明,我们只要向相应的政府机构提供身份证,就可以获取到这些“被认可的”信息。
可以抽象的理解为,政府颁发了一个身份证给个人,当你需要政府服务的时候,只需要提供你的身份证,这样政府就会从它自己维护的数据中,获取你的更多个人信息,提供差异化服务。
身份证会具有有效期,当身份证过期的时候,需要从新申请,否则持有的身份证就会无效。

哪么,可否效仿这个过程,网站服务器“颁发”一个身份证给用户,用户每次访问都带着这个身份证,网站服务器根据这个身份证获取用户更多信息,这样不就可以获取个性化服务了么!当身份证过期的时候,需要用户从新申请。

现在我们要讨论的问题渐渐浮出水面了。我们和服务器之间的交互过程,可以理解为一个Session,直白的翻译就是一次会话,而每次我们携带的身份信息,是通过Cookie的方式实现的。

在《HTTP权威指南中》这样描述了用Cookie实现的身份认证过程。

《Tomcat 服务器 Session的实现》

Cookie的作用就好像服务器,给用户发的一个身份证,上面写着,“我叫XX,地址”,用户访问服务器的时候,携带着这些信息,服务器会读取所有的这些信息。

用户首次访问Web 站点时,服务器对用户一无所知。为了能够在用户下次访问的时候,识别出这个用户,站点给用户“拍上”一个独有的cookie,这样以后服务器就可以识别出这个用户了。Cookie包含了一个由名字=值这样的信息构成的列表,并通过Set-Cookie或者Set-Cookie2 HTTP响应首部返回给用户。浏览器再下次访问站点的时候,会在Cookie请求首部中,原封不动地携带服务器发送过来的Cookie数据。

其实Cookie的可以携带任意信息,它也是服务器维护用户Session的最常用的手段的一种。服务器真对用户的访问,创建一个拥有唯一ID的Session,然后通过Set-Cookie把这个SessionId贴到用户身上。用户每次访问服务器都带着这个SessionId,服务器就可以通过SessionId找出对应的Session对象,识别出用户了。

当然这仍然是很抽象的描述,不同的Web服务器实现这个机制也有很大差异。下面通过Tomcat的源代码来看一下Tomcat作为Web服务器,如何维护Session,如何根据Cookie来获取用户Session的。

Tomcat Session的实现

大概的流程可以分为如下几个步骤。

1. 从Cookie中获取SessionID

针对每一次用户的请求,HTTPProcessor负责解析用户请求,会对HTTP请求头部分析,如果发现存在jsessionid这样的cookie,就把cookie值set到HttpSevletRequest对象中

if (name.equals("cookie")) {
   Cookie cookies[] = RequestUtil.parseCookieHeader(value);
   for (int i = 0; i < cookies.length; i++) {
      if (cookies[i].getName().equals("jsessionid")) {
         // Override anything requested in the URL
         if (!request.isRequestedSessionIdFromCookie()) {
            // Accept only the first session id cookie
            request.setRequestedSessionId(cookies[i].getValue());
            request.setRequestedSessionCookie(true);
            request.setRequestedSessionURL(false);
         }
      }
      request.addCookie(cookies[i]);
   }
}

2. 获取Session,如果不存在创建新Session

当需要使用Session访问其中内容的时候,会调用Request的getSession方法

private HttpSession doGetSession(boolean create) {
        // There cannot be a session if no context has been assigned yet
        if (context == null)
            return (null);

        // Return the current session if it exists and is valid
        if ((session != null) && !session.isValid())
            session = null;
        if (session != null)
            return (session.getSession());

        // Return the requested session if it exists and is valid
        Manager manager = null;
        if (context != null)
            manager = context.getManager();

        if (manager == null)
            return (null);      // Sessions are not supported

        if (requestedSessionId != null) {
            try {
                session = manager.findSession(requestedSessionId);
            } catch (IOException e) {
                session = null;
            }
            if ((session != null) && !session.isValid())
                session = null;
            if (session != null) {
                return (session.getSession());
            }
        }

        // Create a new session if requested and the response is not committed
        if (!create)
            return (null);
        if ((context != null) && (response != null) &&
            context.getCookies() &&
            response.getResponse().isCommitted()) {
            throw new IllegalStateException
              (sm.getString("httpRequestBase.createCommitted"));
        }

        session = manager.createSession();
        if (session != null)
            return (session.getSession());
        else
            return (null);

}

简而言之如果之前设置过SessionId,那么request会在Manager中寻找Session,如果有则直接使用,如果不存在,那么就让Manager创建一个Session。

需要补充一点,Tomcat的Session的创建和维护是通过实现Manager接口实现的,其中StandardManager是标准实现,当然真对不同应用场景,例如集群Tomcat,Session需要各个服务器共享,则需要使用不同的Manager来实现Session的维护。

3. 如果Session存在,在Response中,设置SetCookie
在HTTPResponseBase中的SetHeader方法中,检查Request中Session对象是否存在,如果存在,则把SessionId写在Set-Cookie首部

if ((session != null) && session.isNew() && (getContext() != null) && getContext().getCookies()) {
	Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME, session.getId());
	cookie.setMaxAge(-1);
	String contextPath = null;
	if (context != null)
	    contextPath = context.getPath();
	if ((contextPath != null) && (contextPath.length() > 0))
	    cookie.setPath(contextPath);
	else
	    cookie.setPath("/");
	if (hreq.isSecure())
		cookie.setSecure(true);
	addCookie(cookie);
}

	// Send all specified cookies (if any)
synchronized (cookies) {
	Iterator items = cookies.iterator();
	while (items.hasNext()) {
		Cookie cookie = (Cookie) items.next();
		outputWriter.print(CookieTools.getCookieHeaderName(cookie));
		outputWriter.print(": ");
		outputWriter.print(CookieTools.getCookieHeaderValue(cookie));
		outputWriter.print("\r\n");
	}
}

其实

SESSION_COOKIE_NAME

就是JSESSIONID,至于tomcat为什么使用这个字符串,可能就要和开发tomcat的程序员探讨一下了,其实她可以叫任何名字。
我们注意到在设置Cookie的时候,有一个CookieTools.getCookieHeaderName的方法,该方法根据HTTP版本号,决定使用Set-Cookie还是Set-Cookie2

public static String getCookieHeaderName(Cookie cookie) {
    int version = cookie.getVersion();

    if (version == 1) {
        return "Set-Cookie2";
    } else {
        return "Set-Cookie";
    }
}

 4. 浏览器验证

首先我初次请求服务器,浏览器发送的数据如下:
《Tomcat 服务器 Session的实现》

可以看出来,浏览器发送的Cookie中,不存在JSESSIONID,因此此刻Session还并没有创建和维护起来。

不过在服务器的Response中可以看到Set-Cookie 首部
《Tomcat 服务器 Session的实现》
此时,服务器已经把会话建立起来,并且分配了唯一的标示,并且告诉浏览器:“下次来要告诉我你是谁哦”

然后我们刷一下浏览器,再次请求服务器,可以看到Request如下:
《Tomcat 服务器 Session的实现》
可以看到,这次访问Cookie中已经加上了JSESSIONID,服务器可以利用这个SessionID,从本地找到Session对象,拿到记录下来的用户行为或者用户信息了。

 

 

    原文作者:文西
    原文地址: https://blog.csdn.net/hopeztm/article/details/51549410
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。

相关文章