Tomcat的Session管理机制
>>Session和Cookie请求的过程
Http连接本身是无状态的,即前一次发起的连接跟后一次没有任何关系,是属于两次独立的连接请求,
但是互联网访问基本上都是需要有状态的,即服务器需要知道两次连接请求是不是同一个人访问的。
JSESSIONID是一个唯一标识号,用来标识服务器端的Session,也用来标识客户端的Cookie,客户端和服务器端通过这个JSESSIONID来一一对应。
客户端第一次请求到服务器连接,这个连接是没有附带任何东西的,没有Cookie,没有JSESSIONID。
服务器端接收到请求后,会检查这次请求有没有传过来JSESSIONID或者Cookie,如果没有JSESSIONID和Cookie,则服务器端会创建一个Session,
并生成一个与该Session相关联的JSESSIONID返回给客户端,客户端会保存这个JSESSIONID,并生成一个与该JSESSIONID关联的Cookie。
第二次请求的时候,会把该Cookie(包含JSESSIONID)一起发送给服务器端,
这次服务器发现这个请求有了Cookie,便从中取出JSESSIONID,然后根据这个JSESSIONID找到对应的Session,这样便把Http的无状态连接变成了有状态的连接。
但是有时候浏览器(即客户端)会禁用Cookie,我们知道Cookie是通过Http的请求头部的一个cookie字段传过去的,
如果禁用,那么便得不到这个值,JSESSIONID便不能通过Cookie传入服务器端。
这时可以通过url重写和隐藏表单,url重写就是把JSESSIONID附带在url后面传过去。隐藏表单是在表单提交的时候传入一个隐藏字段JSESSIONID。
>>session的管理机制(转自 tomcat架构分析 (Session管理))
Session管理主要涉及到这几个方面:
创建session
注销session
持久化及启动加载session
Tomcat通过每个context容器内的一个Manager对象来管理session。
这个manager对象可以根据tomcat提供的接口或基类来自己定制,也可以使用Tomcat的标准实现。
Tomcat中的session管理主要在org.apache.catalina.session包中实现。
1.创建session
在具体说明session的创建过程之前,先看一下BS访问模型吧,这样理解直观一点。
browser发送Http request;
tomcat内核Http11Processor会从HTTP request中解析出“jsessionid”(具体的解析过程为先从request的URL中解析,这是为了有的浏览器把cookie功能禁止后,将URL重写考虑的,如果解析不出来,再从cookie中解析相应的jsessionid),解析完后封装成一个request对象(当然还有其他的http header);
servlet中获取session,其过程是根据刚才解析得到的jsessionid(如果有的话),从session池(session maps)中获取相应的session对象;这个地方有个逻辑,就是如果jsessionid为空的话(或者没有其对应的session对象,或者有session对象,但此对象已经过期超时),可以选择创建一个session,或者不创建;
如果创建新session,则将session放入session池中,同时将与其相对应的jsessionid写入cookie通过Http response header的方式发送给browser,然后重复第一步。
以上是session的获取及创建过程。在servlet中获取session,通常是调用request的getSession方法。这个方法需要传入一个boolean参数,这个参数就是实现刚才说的,当jsessionid为空或从session池中获取不到相应的session对象时,选择创建一个新的session还是不创建。
看一下核心代码逻辑;
protected Session doGetSession(boolean create) { …… // 先获取所在context的manager对象 Manager manager = null; if (context != null) manager = context.getManager(); if (manager == null) return (null); // Sessions are not supported //这个requestedSessionId就是从Http request中解析出来的 if (requestedSessionId != null) { try { //manager管理的session池中找相应的session对象 session = manager.findSession(requestedSessionId); } catch (IOException e) { session = null; } //判断session是否为空及是否过期超时 if ((session != null) && !session.isValid()) session = null; if (session != null) { //session对象有效,记录此次访问时间 session.access(); return (session); } } // 如果参数是false,则不创建新session对象了,直接退出了 if (!create) return (null); if ((context != null) && (response != null) && context.getCookies() && response.getResponse().isCommitted()) { throw new IllegalStateException (sm.getString("coyoteRequest.sessionCreateCommitted")); } // 开始创建新session对象 if (connector.getEmptySessionPath() && isRequestedSessionIdFromCookie()) { session = manager.createSession(getRequestedSessionId()); } else { session = manager.createSession(null); } // 将新session的jsessionid写入cookie,传给browser if ((session != null) && (getContext() != null) && getContext().getCookies()) { Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME, session.getIdInternal()); configureSessionCookie(cookie); response.addCookieInternal(cookie); } //记录session最新访问时间 if (session != null) { session.access(); return (session); } else { return (null); } }
尽管不能贴出所有代码,但是上述的核心逻辑还是很清晰的。从中也可以看出,我们经常在servlet中这两种调用方式的不同;
新创建session request.getSession(); 或者request.getSession(true); 不创建session request.getSession(false);
接下来,看一下StandardManager的createSession方法,了解一下session的创建过程;
public Session createSession(String sessionId) { 是个session数量控制逻辑,超过上限则抛异常退出 if ((maxActiveSessions >= 0) && (sessions.size() >= maxActiveSessions)) { rejectedSessions++; throw new IllegalStateException (sm.getString("standardManager.createSession.ise")); } return (super.createSession(sessionId)); }
这个最大支持session数量maxActiveSessions是可以配置的,先不管这个安全控制逻辑,看其主逻辑,即调用其基类的createSession方法;
public Session createSession(String sessionId) { // 创建一个新的StandardSession对象 Session session = createEmptySession(); // Initialize the properties of the new session and return it session.setNew(true); session.setValid(true); session.setCreationTime(System.currentTimeMillis()); session.setMaxInactiveInterval(this.maxInactiveInterval); if (sessionId == null) { //设置jsessionid sessionId = generateSessionId(); } session.setId(sessionId); sessionCounter++; return (session); }
关键是jsessionid的产生过程,接着看generateSessionId方法;
protected synchronized String generateSessionId() { byte random[] = new byte[16]; String jvmRoute = getJvmRoute(); String result = null; // Render the result as a String of hexadecimal digits StringBuffer buffer = new StringBuffer(); do { int resultLenBytes = 0; if (result != null) { buffer = new StringBuffer(); duplicates++; } while (resultLenBytes < this.sessionIdLength) { getRandomBytes(random); random = getDigest().digest(random); for (int j = 0; j < random.length && resultLenBytes < this.sessionIdLength; j++) { byte b1 = (byte) ((random[j] & 0xf0) >> 4); byte b2 = (byte) (random[j] & 0x0f); if (b1 < 10) buffer.append((char) ('0' + b1)); else buffer.append((char) ('A' + (b1 - 10))); if (b2 < 10) buffer.append((char) ('0' + b2)); else buffer.append((char) ('A' + (b2 - 10))); resultLenBytes++; } } if (jvmRoute != null) { buffer.append('.').append(jvmRoute); } result = buffer.toString(); //注意这个do…while结构 } while (sessions.containsKey(result)); return (result); }
这里主要说明的不是生成jsessionid的算法了,而是这个do…while结构。把这个逻辑抽象出来,可以看出;
如图所示,创建jsessionid的方式是由tomcat内置的加密算法算出一个随机的jsessionid,如果此jsessionid已经存在,则重新计算一个新的,直到确保现在计算的jsessionid唯一。
好了,至此一个session就这么创建了,像上面所说的,返回时是将jsessionid以HTTP response的header:“Set-cookie”发给客户端。
2.注销session
主动注销
Session创建完之后,不会一直存在,或是主动注销,或是超时清除。即是出于安全考虑也是为了节省内存空间等。
例如,常见场景:用户登出系统时,会主动触发注销操作。
主动注销时,是调用标准的servlet接口:
session.invalidate();
看一下tomcat提供的标准session实现(StandardSession)
public void invalidate() { if (!isValidInternal()) throw new IllegalStateException (sm.getString("standardSession.invalidate.ise")); // 明显的注销方法 expire(); }
Expire方法的逻辑稍后再说,先看看超时注销,因为它们调用的是同一个expire方法。
超时注销
Tomcat定义了一个最大空闲超时时间,也就是说当session没有被操作超过这个最大空闲时间时间时,再次操作这个session,这个session就会触发expire。
这个方法封装在StandardSession中的isValid()方法内,这个方法在获取这个request请求对应的session对象时调用,可以参看上面说的创建session环节。
也就是说,获取session的逻辑是,先从manager控制的session池中获取对应jsessionid的session对象,如果获取到,就再判断是否超时,如果超时,就expire这个session了。
看一下tomcat提供的标准session实现(StandardSession)
public boolean isValid() { …… //这就是判断距离上次访问是否超时的过程 if (maxInactiveInterval >= 0) { long timeNow = System.currentTimeMillis(); int timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L); if (timeIdle >= maxInactiveInterval) { expire(true); } } return (this.isValid); }
Expire方法
是时候来看看expire方法了。
public void expire(boolean notify) { synchronized (this) { ...... //设立标志位 setValid(false); //计算一些统计值,例如此manager下所有session平均存活时间等 long timeNow = System.currentTimeMillis(); int timeAlive = (int) ((timeNow - creationTime)/1000); synchronized (manager) { if (timeAlive > manager.getSessionMaxAliveTime()) { manager.setSessionMaxAliveTime(timeAlive); } int numExpired = manager.getExpiredSessions(); numExpired++; manager.setExpiredSessions(numExpired); int average = manager.getSessionAverageAliveTime(); average = ((average * (numExpired-1)) + timeAlive)/numExpired; manager.setSessionAverageAliveTime(average); } // 将此session从manager对象的session池中删除 manager.remove(this); ...... } }
不需要解释,已经很清晰了。
这个超时时间是可以配置的,缺省在tomcat的全局web.xml下配置,也可在各个app下的web.xml自行定义;
<session-config> <session-timeout>30</session-timeout> </session-config>
单位是分钟。
3.Session持久化及启动初始化
这个功能主要是,当tomcat执行安全退出时(通过执行shutdown脚本),会将session持久化到本地文件,通常在tomcat的部署目录下有个session.ser文件。
当启动tomcat时,会从这个文件读入session,并添加到manager的session池中去。
这样,当tomcat正常重启时, session没有丢失,对于用户而言,体会不到重启,不影响用户体验。
看一下概念图吧,觉得不是重要实现逻辑,代码就不说了。
由此可以看出,session的管理是容器层做的事情,应用层一般不会参与session的管理,也就是说,如果在应用层获取到相应的session,已经是由tomcat提供的,
因此如果过多的依赖session机制来进行一些操作,例如访问控制,安全登录等就不是十分的安全,因为如果有人能得到正在使用的jsessionid,则就可以侵入系统。
Tomcat的Session管理机制的更多相关文章
- go web编程——session管理机制设计与实现
原生Go语言没有实现session管理机制,所以如果使用原生Go语言进行web编程,我们需要自己进行session管理机制的设计与实现,本文将就此进行详细介绍,并实现一个简单的session管理机制. ...
- Tomcat源码分析——Session管理分析(上)
前言 对于广大java开发者而已,对于J2EE规范中的Session应该并不陌生,我们可以使用Session管理用户的会话信息,最常见的就是拿Session用来存放用户登录.身份.权限及状态等信息.对 ...
- 微软与开源干货对比篇_PHP和 ASP.NET在 Session实现和管理机制上差异
微软与开源干货对比篇_PHP和 ASP.NET在 Session实现和管理机制上差异 前言:由于开发人员要靠工具吃饭,可能和开发工具.语言.环境呆的时间比和老婆孩子亲人在一起的时间还多,所以每个人或多 ...
- TOMCAT8源码分析——SESSION管理分析(上)
前言 对于广大java开发者而已,对于J2EE规范中的Session应该并不陌生,我们可以使用Session管理用户的会话信息,最常见的就是拿Session用来存放用户登录.身份.权限及状态等信息.对 ...
- 使用Memcached Session Manager扩展Session管理
>>Tomcat的session管理 在请求过程中首先要解析请求中的sessionId信息,然后将sessionId存储到request的参数列表中. 然后再从request获取sessi ...
- Spring aop与HibernateTemplate——session管理(每事务一次 Session)
一.HibernateTemplate与Spring aop简介 参见http://bbs.csdn.net/topics/340207475中网友blueram的发言.(感谢blueram) 二.在 ...
- 细说shiro之六:session管理
官网:https://shiro.apache.org/ 我们先来看一下shiro中关于Session和Session Manager的类图. 如上图所示,shiro自己定义了一个新的Session接 ...
- Tomcat中session的管理机制
1. 请求过程中的session操作: 简述:在请求过程中首先要解析请求中的sessionId信息,然后将sessionId存储到request的参数列表中.然后再从 request获取s ...
- tomcat之Session的管理
Session是由服务器端的应用服务器容器(如Tomcat.Jetty)存储的.下面分析一下Tomcat是如何管理Session的. 转自:tomcat架构分析 (Session管理) Tomcat中 ...
随机推荐
- jquery版瀑布流
一个月前用jquery实现了瀑布流效果,看着当时的代码有点难过……今天抽时间稍微修改了一下.额,现在看起来不是那么难受了,就来和大家分享一下.废话不多说,开始正题~ 一.演示效果 二.html代码 & ...
- MySQL分配角色权限
1.创建新用户 通过root用户登录之后创建 >> grant all privileges on *.* to testuser@localhost identified by &quo ...
- PHP中interface与 implements 关键字
类中接口的应用 1.关键字:interface 2.关键字:implements 1.接口的介绍与创建 接口:一种成员属性全部为抽象或常量的特殊抽象类. 规则: 1.类中全部为抽象方法. 2.抽象方法 ...
- linux kernel 杂谈
首先介绍一下背景吧,工作三个星期了.复习了一波u-boot,跟了一下事件上报,搞了下平台设备,扣了一个内存检查代码. 想想生活是不是有点无聊.对啊,真的很无聊!!!! 无聊也没有办法啊,所以找点方法去 ...
- livezilla账号或密码修改方法
livezilla的账号和密码不在数据库,保存在php文件里面. 今天想修改一下网站livezilla系统管理员账号和密码,去数据库找了半天没找到,推测可能是存在文件中.搜索了一下,果然是在livez ...
- 3.7---猫狗收容所(CC150)
解答的思路:建立一个queue放狗,一个queue放猫. 如下: import java.util.*; class Dog{ int time; int value; Dog(int a, int ...
- ZendStudio 解决svn导出项目乱码问题
从svn导出项目往往会出现乱码,可以右击项目,点击properties(或者选中项目alt+enter键进入)直接修改项目编码为utf-8,但是html文件还是乱码. 下面的方法可以解决: windo ...
- PHP MVC 中的MODEL层
Model层,就是MVC模式中的数据处理层,用来进行数据和商业逻辑的装封 三.实现你的Mode层 Model层,就是MVC模式中的数据处理层,用来进行数据和商业逻辑的装封,进行他的设计的时候设计到三个 ...
- ASC47B borderless
题目描述 border集合为{NULL,str}的串str称为borderless串. border即KMP里的那个. 字符集{'a','b'},给定长度n,求第k(给定)小的borderless串. ...
- SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
在用php返回json数据的时候如果出现这种错误,先检查一下php中是否有使用var_dump()函数 这个函数会在页面输出测试变量的结构,浏览器会将这个当做json数据,所以就报错了....