Session管理是JavaEE容器比较重要的一部分,在app中也经常会用到。在开发app时,我们只是获取一个session,然后向session中存取数据,然后再销毁session。那么如何产生session,以及session池如何维护及管理,这些并没有在app涉及到。这些工作都是由容器来完成的。 
Tomcat中主要由每个context容器内的一个Manager对象来管理session。对于这个manager对象的实现,可以根据tomcat提供的接口或基类来自己定制,同时,tomcat也提供了标准实现。 
在tomcat架构分析(容器类)中已经介绍过,在每个context对象,即web app都具有一个独立的manager对象。通过server.xml可以配置定制化的manager,也可以不配置。不管怎样,在生成context对象时,都会生成一个manager对象。缺省的是StandardManager类,其类路径为:

引用
org.apache.catalina.session.StandardManager

Session对象也可以定制化实现,其主要实现标准servlet的session接口:

引用
javax.servlet.http.HttpSession

Tomcat也提供了标准的session实现:

引用
org.apache.catalina.session.StandardSession

本文主要就是结合消息流程介绍这两个类的实现,及session机制。 
Session方面牵涉的东西还是蛮多的,例如HA,session复制是其中重要部分等,不过本篇主要从功能方面介绍session管理,有时间再说说扩展。 
Session管理主要涉及到这几个方面:

  • 创建session
  • 注销session
  • 持久化及启动加载session

创建session 
在具体说明session的创建过程之前,先看一下BS访问模型吧,这样理解直观一点。 

  1. browser发送Http request;
  2. tomcat内核Http11Processor会从HTTP request中解析出“jsessionid”(具体的解析过程为先从request的URL中解析,这是为了有的浏览器把cookie功能禁止后,将URL重写考虑的,如果解析不出来,再从cookie中解析相应的jsessionid),解析完后封装成一个request对象(当然还有其他的http header);
  3. servlet中获取session,其过程是根据刚才解析得到的jsessionid(如果有的话),从session池(session maps)中获取相应的session对象;这个地方有个逻辑,就是如果jsessionid为空的话(或者没有其对应的session对象,或者有session对象,但此对象已经过期超时),可以选择创建一个session,或者不创建;
  4. 如果创建新session,则将session放入session池中,同时将与其相对应的jsessionid写入cookie通过Http response header的方式发送给browser,然后重复第一步。

以上是session的获取及创建过程。在servlet中获取session,通常是调用request的getSession方法。这个方法需要传入一个boolean参数,这个参数就是实现刚才说的,当jsessionid为空或从session池中获取不到相应的session对象时,选择创建一个新的session还是不创建。 
看一下核心代码逻辑;

  1. protected Session doGetSession(boolean create) {
  2. ……
  3. // 先获取所在context的manager对象
  4. Manager manager = null;
  5. if (context != null)
  6. manager = context.getManager();
  7. if (manager == null)
  8. return (null);      // Sessions are not supported
  9. //这个requestedSessionId就是从Http request中解析出来的
  10. if (requestedSessionId != null) {
  11. try {
  12. //manager管理的session池中找相应的session对象
  13. session = manager.findSession(requestedSessionId);
  14. } catch (IOException e) {
  15. session = null;
  16. }
  17. //判断session是否为空及是否过期超时
  18. if ((session != null) && !session.isValid())
  19. session = null;
  20. if (session != null) {
  21. //session对象有效,记录此次访问时间
  22. session.access();
  23. return (session);
  24. }
  25. }
  26. // 如果参数是false,则不创建新session对象了,直接退出了
  27. if (!create)
  28. return (null);
  29. if ((context != null) && (response != null) &&
  30. context.getCookies() &&
  31. response.getResponse().isCommitted()) {
  32. throw new IllegalStateException
  33. (sm.getString("coyoteRequest.sessionCreateCommitted"));
  34. }
  35. // 开始创建新session对象
  36. if (connector.getEmptySessionPath()
  37. && isRequestedSessionIdFromCookie()) {
  38. session = manager.createSession(getRequestedSessionId());
  39. } else {
  40. session = manager.createSession(null);
  41. }
  42. // 将新session的jsessionid写入cookie,传给browser
  43. if ((session != null) && (getContext() != null)
  44. && getContext().getCookies()) {
  45. Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,
  46. session.getIdInternal());
  47. configureSessionCookie(cookie);
  48. response.addCookieInternal(cookie);
  49. }
  50. //记录session最新访问时间
  51. if (session != null) {
  52. session.access();
  53. return (session);
  54. } else {
  55. return (null);
  56. }
  57. }

尽管不能贴出所有代码,但是上述的核心逻辑还是很清晰的。从中也可以看出,我们经常在servlet中这两种调用方式的不同; 
新创建session

引用
request.getSession(); 或者request.getSession(true);

不创建session

引用
request.getSession(false);

接下来,看一下StandardManager的createSession方法,了解一下session的创建过程;

  1. public Session createSession(String sessionId) {
  2. 是个session数量控制逻辑,超过上限则抛异常退出
  3. if ((maxActiveSessions >= 0) &&
  4. (sessions.size() >= maxActiveSessions)) {
  5. rejectedSessions++;
  6. throw new IllegalStateException
  7. (sm.getString("standardManager.createSession.ise"));
  8. }
  9. return (super.createSession(sessionId));
  10. }

这个最大支持session数量maxActiveSessions是可以配置的,先不管这个安全控制逻辑,看其主逻辑,即调用其基类的createSession方法;

  1. public Session createSession(String sessionId) {
  2. // 创建一个新的StandardSession对象
  3. Session session = createEmptySession();
  4. // Initialize the properties of the new session and return it
  5. session.setNew(true);
  6. session.setValid(true);
  7. session.setCreationTime(System.currentTimeMillis());
  8. session.setMaxInactiveInterval(this.maxInactiveInterval);
  9. if (sessionId == null) {
  10. //设置jsessionid
  11. sessionId = generateSessionId();
  12. }
  13. session.setId(sessionId);
  14. sessionCounter++;
  15. return (session);
  16. }

关键是jsessionid的产生过程,接着看generateSessionId方法;

  1. protected synchronized String generateSessionId() {
  2. byte random[] = new byte[16];
  3. String jvmRoute = getJvmRoute();
  4. String result = null;
  5. // Render the result as a String of hexadecimal digits
  6. StringBuffer buffer = new StringBuffer();
  7. do {
  8. int resultLenBytes = 0;
  9. if (result != null) {
  10. buffer = new StringBuffer();
  11. duplicates++;
  12. }
  13. while (resultLenBytes < this.sessionIdLength) {
  14. getRandomBytes(random);
  15. random = getDigest().digest(random);
  16. for (int j = 0;
  17. j < random.length && resultLenBytes < this.sessionIdLength;
  18. j++) {
  19. byte b1 = (byte) ((random[j] & 0xf0) >> 4);
  20. byte b2 = (byte) (random[j] & 0x0f);
  21. if (b1 < 10)
  22. buffer.append((char) ('0' + b1));
  23. else
  24. buffer.append((char) ('A' + (b1 - 10)));
  25. if (b2 < 10)
  26. buffer.append((char) ('0' + b2));
  27. else
  28. buffer.append((char) ('A' + (b2 - 10)));
  29. resultLenBytes++;
  30. }
  31. }
  32. if (jvmRoute != null) {
  33. buffer.append('.').append(jvmRoute);
  34. }
  35. result = buffer.toString();
  36. //注意这个do…while结构
  37. } while (sessions.containsKey(result));
  38. return (result);
  39. }

这里主要说明的不是生成jsessionid的算法了,而是这个do…while结构。把这个逻辑抽象出来,可以看出; 
 
如图所示,创建jsessionid的方式是由tomcat内置的加密算法算出一个随机的jsessionid,如果此jsessionid已经存在,则重新计算一个新的,直到确保现在计算的jsessionid唯一。 
好了,至此一个session就这么创建了,像上面所说的,返回时是将jsessionid以HTTP response的header:“Set-cookie”发给客户端。 
注销session

  • 主动注销
  • 超时注销

Session创建完之后,不会一直存在,或是主动注销,或是超时清除。即是出于安全考虑也是为了节省内存空间等。例如,常见场景:用户登出系统时,会主动触发注销操作。 
主动注销 
主动注销时,是调用标准的servlet接口:

引用
session.invalidate();

看一下tomcat提供的标准session实现(StandardSession)

  1. public void invalidate() {
  2. if (!isValidInternal())
  3. throw new IllegalStateException
  4. (sm.getString("standardSession.invalidate.ise"));
  5. // 明显的注销方法
  6. expire();
  7. }

Expire方法的逻辑稍后再说,先看看超时注销,因为它们调用的是同一个expire方法。 
超时注销 
Tomcat定义了一个最大空闲超时时间,也就是说当session没有被操作超过这个最大空闲时间时间时,再次操作这个session,这个session就会触发expire。 
这个方法封装在StandardSession中的isValid()方法内,这个方法在获取这个request请求对应的session对象时调用,可以参看上面说的创建session环节。也就是说,获取session的逻辑是,先从manager控制的session池中获取对应jsessionid的session对象,如果获取到,就再判断是否超时,如果超时,就expire这个session了。 
看一下tomcat提供的标准session实现(StandardSession)

  1. public boolean isValid() {
  2. ……
  3. //这就是判断距离上次访问是否超时的过程
  4. if (maxInactiveInterval >= 0) {
  5. long timeNow = System.currentTimeMillis();
  6. int timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L);
  7. if (timeIdle >= maxInactiveInterval) {
  8. expire(true);
  9. }
  10. }
  11. return (this.isValid);
  12. }

Expire方法 
是时候来看看expire方法了。

  1. public void expire(boolean notify) {
  2. synchronized (this) {
  3. ......
  4. //设立标志位
  5. setValid(false);
  6. //计算一些统计值,例如此manager下所有session平均存活时间等
  7. long timeNow = System.currentTimeMillis();
  8. int timeAlive = (int) ((timeNow - creationTime)/1000);
  9. synchronized (manager) {
  10. if (timeAlive > manager.getSessionMaxAliveTime()) {
  11. manager.setSessionMaxAliveTime(timeAlive);
  12. }
  13. int numExpired = manager.getExpiredSessions();
  14. numExpired++;
  15. manager.setExpiredSessions(numExpired);
  16. int average = manager.getSessionAverageAliveTime();
  17. average = ((average * (numExpired-1)) + timeAlive)/numExpired;
  18. manager.setSessionAverageAliveTime(average);
  19. }
  20. // 将此session从manager对象的session池中删除
  21. manager.remove(this);
  22. ......
  23. }
  24. }

不需要解释,已经很清晰了。 
这个超时时间是可以配置的,缺省在tomcat的全局web.xml下配置,也可在各个app下的web.xml自行定义;

  1. <session-config>
  2. <session-timeout>30</session-timeout>
  3. </session-config>

单位是分钟。 
Session持久化及启动初始化 
这个功能主要是,当tomcat执行安全退出时(通过执行shutdown脚本),会将session持久化到本地文件,通常在tomcat的部署目录下有个session.ser文件。当启动tomcat时,会从这个文件读入session,并添加到manager的session池中去。 
这样,当tomcat正常重启时, session没有丢失,对于用户而言,体会不到重启,不影响用户体验。 
看一下概念图吧,觉得不是重要实现逻辑,代码就不说了。 
 
总结 
由此可以看出,session的管理是容器层做的事情,应用层一般不会参与session的管理,也就是说,如果在应用层获取到相应的session,已经是由tomcat提供的,因此如果过多的依赖session机制来进行一些操作,例如访问控制,安全登录等就不是十分的安全,因为如果有人能得到正在使用的jsessionid,则就可以侵入系统。

http://gearever.iteye.com/blog/1546423

Tomcat容器的Session管理的更多相关文章

  1. tomcat架构分析 (Session管理)

    Session管理是JavaEE容器比较重要的一部分,在app中也经常会用到.在开发app时,我们只是获取一个session,然后向session中存取数据,然后再销毁session.那么如何产生se ...

  2. How Tomcat works — 八、tomcat中的session管理

    在使用shiro的session的时候感觉对于tomcat中session的管理还不是特别清楚,而且session管理作为tomcat中比较重要的一部分还是很有必要学习的. 目录 概述 session ...

  3. [转]tomcat中的session管理

    转载地址:http://blog.csdn.net/iloveqing/article/details/1544958 当一个sesson开始时,Servlet容器会创建一个HttpSession对象 ...

  4. 【Tomcat】Tomcat集群session管理

    网上资料汇总: 关于 tomcat 集群中 session 共享的三种方法 Tomcat7集群共享Session 基于redis进行统一管理

  5. Tomcat的Session管理机制

    >>Session和Cookie请求的过程 Http连接本身是无状态的,即前一次发起的连接跟后一次没有任何关系,是属于两次独立的连接请求,但是互联网访问基本上都是需要有状态的,即服务器需要 ...

  6. Tomcat源码分析——Session管理分析(下)

    前言 在<TOMCAT源码分析——SESSION管理分析(上)>一文中我介绍了Session.Session管理器,还以StandardManager为例介绍了Session管理器的初始化 ...

  7. Tomcat源码分析——Session管理分析(上)

    前言 对于广大java开发者而已,对于J2EE规范中的Session应该并不陌生,我们可以使用Session管理用户的会话信息,最常见的就是拿Session用来存放用户登录.身份.权限及状态等信息.对 ...

  8. Session or Cookie?是否需要用Tomcat等Web容器的Session

    Cookie是HTTP协议标准下的存储用户信息的工具,浏览器把用户信息存放到本地的文本文件中. Session是基于Cookie实现的. 2011年4月,武汉群硕面试的时候(实习生),面试官也问过这个 ...

  9. Redis3.2+Tomcat实现集群的Session管理 -- tomcat-redis-session-manager的编译和开发部署环境搭建

    已经有不少文章介绍使用tomcat-redis-session-manager来实现Redis存储Tomcat的Session,实现分布式Session管理.但是现在官方编译的tomcat-redis ...

随机推荐

  1. 【PostgreSQL-9.6.3】表继承

    表继承是PostgreSQL特有的,子表可以从父表中继承字段和一些属性.例如: --创建一张表“persons”作为父表: test=# create table persons ( test(# i ...

  2. 【备份工具】mydumper

    Mydumper主要特性:是一个针对MySQL的高性能多线程备份和恢复工具,开发人员主要来自MySQL,Facebook,SkySQL公司. 特性: 1:轻量级C语言写的 2:执行速度比mysqldu ...

  3. POJ_2063_完全背包

    Investment Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 10090   Accepted: 3540 Descr ...

  4. element-ui按需引入

    { "name": "vue-test2", "description": "A Vue.js project", &q ...

  5. python sqlalthemy 总结

    orm 数据状态的预知识 瞬时状态:刚创建的对象还没有被Session持久化.缓存中不存在这个对象的数据并且数据库中没有这个对象对应的数据为瞬时状态这个时候是没有OID. 持久状态:对象经过Sessi ...

  6. 【笔记】Linux就该这么学-第六课第四章

    vim使用    a 在光标后一位置插入    i 在光标当前位置插入    o 在光标下面创建个空行    dd 删除(剪切)光标所在行    5dd 删除(剪切)从光标处开始的5行    yy 复 ...

  7. C#学习笔记_01_基础内容

    01_基础内容 进(位)制 十进制:逢10进1,数字由0-9组成: 二进制:逢2进1,数字由0-1组成: 八进制:逢8进1,数字由0-7组成: 十六进制:逢16进1,数字由0-9和a-f组成: 进制转 ...

  8. Python语言认识和实用工具(1)

    目录 1. Python语言概述 2. Python使用注意事项 3. Python学习工具 1. Python语言概述   Python是一种计算机程序设计语言.是一种动态的.面向对象的脚本语言,语 ...

  9. Codeforces 816A/B

    A. Karen and Morning 传送门:http://codeforces.com/contest/816/problem/A 水题,参考程序如下: #include <stdio.h ...

  10. 《团队名称》第八次团队作业:Alpha冲刺

    项目 内容 这个作业属于哪个课程 任课教师博客主页链接 这个作业的要求在哪里 作业链接地址 团队名称 代码敲不队 作业学习目标 (1)掌握软件测试基础技术(2)学习迭代式增量软件开发过程(Scrum) ...