tomcat之Session的管理
Session是由服务器端的应用服务器容器(如Tomcat、Jetty)存储的。下面分析一下Tomcat是如何管理Session的。
Tomcat中主要由每个context容器内的一个Manager对象来管理session。对于这个manager对象的实现,可以根据tomcat提供的接口或基类来自己定制,同时,tomcat也提供了标准实现。
在每个context对象,即web app都具有一个独立的manager对象。通过server.xml可以配置定制化的manager,也可以不配置。不管怎样,在生成context对象时,都会生成一个manager对象。缺省的是StandardManager类,其类路径为:
Session对象也可以定制化实现,其主要实现标准servlet的session接口:
Tomcat也提供了标准的session实现:
Session管理主要涉及到这几个方面:
- 创建session
- 注销session
- 持久化及启动加载session
创建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
不创建session
接下来,看一下StandardManager的createSession方法,了解一下session的创建过程;
- public Session createSession(String sessionId) {
- 是个session数量控制逻辑,超过上限则抛异常退出
- ) &&
- (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() {
- ];
- String jvmRoute = getJvmRoute();
- String result = null;
- // Render the result as a String of hexadecimal digits
- StringBuffer buffer = new StringBuffer();
- do {
- ;
- if (result != null) {
- buffer = new StringBuffer();
- duplicates++;
- }
- while (resultLenBytes < this.sessionIdLength) {
- getRandomBytes(random);
- random = getDigest().digest(random);
- ;
- j < random.length && resultLenBytes < this.sessionIdLength;
- j++) {
- );
- byte b2 = (byte) (random[j] & 0x0f);
- )
- buffer.append((char) ('0' + b1));
- else
- )));
- )
- buffer.append((char) ('0' + b2));
- else
- )));
- 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”发给客户端。
注销session
- 主动注销
- 超时注销
Session创建完之后,不会一直存在,或是主动注销,或是超时清除。即是出于安全考虑也是为了节省内存空间等。例如,常见场景:用户登出系统时,会主动触发注销操作。
主动注销
主动注销时,是调用标准的servlet接口:
看一下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() {
- ……
- //这就是判断距离上次访问是否超时的过程
- ) {
- 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();
- );
- synchronized (manager) {
- if (timeAlive > manager.getSessionMaxAliveTime()) {
- manager.setSessionMaxAliveTime(timeAlive);
- }
- int numExpired = manager.getExpiredSessions();
- numExpired++;
- manager.setExpiredSessions(numExpired);
- int average = manager.getSessionAverageAliveTime();
- )) + 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>
单位是分钟。
Session持久化及启动初始化
这个功能主要是,当tomcat执行安全退出时(通过执行shutdown脚本),会将session持久化到本地文件,通常在tomcat的部署目录下有个session.ser文件。当启动tomcat时,会从这个文件读入session,并添加到manager的session池中去。
这样,当tomcat正常重启时, session没有丢失,对于用户而言,体会不到重启,不影响用户体验。
看一下概念图吧,觉得不是重要实现逻辑,代码就不说了。
总结
由此可以看出,session的管理是容器层做的事情,应用层一般不会参与session的管理,也就是说,如果在应用层获取到相应的session,已经是由tomcat提供的,因此如果过多的依赖session机制来进行一些操作,例如访问控制,安全登录等就不是十分的安全,因为如果有人能得到正在使用的jsessionid,则就可以侵入系统。
自己的个人博客:bingtel-木犹如此的博客
tomcat之Session的管理的更多相关文章
- Nginx+Tomcat关于Session的管理
前言 Nginx+Tomcat对Session的管理一直有了解,但是一直没有实际操作一遍,本文从最简单的安装启动开始,通过实例的方式循序渐进的介绍了几种管理session的方式. nginx安装配置 ...
- Tomcat中session的管理机制
1. 请求过程中的session操作: 简述:在请求过程中首先要解析请求中的sessionId信息,然后将sessionId存储到request的参数列表中.然后再从 request获取s ...
- tomcat中session在两个webapp中实现共享
现在遇到一个需求就是要求完成简单的单点登录,通过在一个tomcat实例中放置两个webapps应用ROOT应用和CEO应用来完成在ROOT应用登录后,在CEO可以直接使用,而未在ROOT应用登录时,不 ...
- Tomcat的Session管理机制
>>Session和Cookie请求的过程 Http连接本身是无状态的,即前一次发起的连接跟后一次没有任何关系,是属于两次独立的连接请求,但是互联网访问基本上都是需要有状态的,即服务器需要 ...
- Tomcat使用Memcached Session Manager管理Session
Tomcat使用Memcached Session Manager管理Session 废话不多说,直接进入主题.项目使用阿里云负载均衡+ECS服务器集群进行部署,Tomcat使用8.5版本.阿里云负载 ...
- tomcat cluster session同步时保存map数据遇到的问题
Tomcat Cluster官网:https://tomcat.apache.org/tomcat-7.0-doc/cluster-howto.html(tomcat7.0) 场景: tomcat1 ...
- (转)tomcat架构&session共享
(二期)16.tomcat的整体架构与session共享方案 [课程16]tomcat...共享.xmind47.6KB [课程16]tomcat...流程.xmind0.6MB [课程16]tomc ...
- Tomcat 之session 持久化1
Tomcat 之session 持久化原理 几个概念: Manager 接口,其实就是指的是对 其Sesison 的管理, 其默认实现是StandardManager (内部没有任何Store对象实 ...
- Cookie 和 Session 的区别和联系?session的生命周期?多个服务器部署session的管理?
一.session 和 cookie 1.cookie Cookie会根据响应报文里的一个叫做Set-Cookie的首部字段信息,通知客户端保存Cookie.当下次客户端再向服务端发起请求时,客户端会 ...
随机推荐
- monkeyrunner脚本录制
1.在窗口输入 monkeyrunner monkey_recorder.py 调用录制脚本工具 2.在窗口输入 monkeyrunner monkey_playback.py d:\game ...
- 【安装Redis】CentOS7 下安装NodeJs+Express+MongoDB+Redis
Redis,V3.2,官网l官方链接:http://www.redis.io/download,参考:http://blog.csdn.net/mlks_2008/article/details/19 ...
- ng-repeat
<html ng-app="compile"> <head> <script src="http://apps.bdimg.com/libs ...
- Ubuntu和Win双系统解决主板时间差
由于U和W两个系统的默认时间相差8小时,且会自动改变电脑主板里的默认时间,需要在U中进行设置. timedatectl set-local-rtc true
- JS多线程(web work)
JS多线程JS多线程不允许操作DOM 1. 引用Concurrent Thread.js库用法:Concurrent.Thread.Create(function(){};) 2. Web Workh ...
- PHPExcel 大数据的导出
PHPExcel 是一个php语言读取导出数据.导入生成Excel的类库,使用起来非常方便,但有时会遇到以些问题,比如导出的数据超时,内存溢出等. 下面我们来说说这些问题和解决办法. PHPExcel ...
- 第三十三篇:使用uiresImporter生成uires.idx及skin.xml
在SOUI中,使用uires.idx这个文件来记录程序中使用的所有资源文件. 此外绘制对象(ISkinObj)则一般放在skin.xml中描述. 要向一个界面中增加一个新的图片,在没有uiresImp ...
- 结合谷歌地图多边形(polygon)与Sql Server 2008的空间数据类型计算某个点是否在多边形内的注意事项
首先在利用 GEOGRAPHY::STPolyFromText(@GeoStr, 4326) 这样的函数把字符串转换为Geography类型时,字符串里经纬度的顺序是 “经度[空格]纬度”,即“lon ...
- js获取css中的样式
众所周知,obj.style只能够获取 <div id="a" style="width:100px;"></div> 结构上的样式 如 ...
- JS(去掉前后空格或去掉所有空格)的用法
1. 去掉字符串前后所有空格: 代码如下: function Trim(str) { return str.replace(/(^\s*)|(\s*$)/g, ""); } 说明 ...