的关于session失效的几种情况

      一直都没怎么记录技术文章的日志了。这回个小问题花了比较久的时间,感觉还是记录下来以便以后查阅和帮助遇到同类问题的朋。其实在网上一搜session丢失,大把的都是讲session超时和跨域的iframe的问题。其实我碰到的问题和这些都不是。首先来描述下问题的情况吧。
     1首页index.jsp登陆----》2登陆action创建新session---》3返回到一个有装饰器的页面----》4点一级菜单,开始报错找不到session里的值。
    以上情况在外网发生较多。局域网很少发生。
    分析1:是否session没有在登陆时创建成功?  通过环节3,有个从session中取值的过程。正常,排除
    分析2:是否登陆后session超时或者被销毁?通过加入自己的session监听器观察(继承HttpSessionListener),没有发现有session的销毁

     问题就应该在jsessionId的赋值问题上。web程序走http协议是没状态的,session更客户端的关联是通过jsessionId来维持的这个大家都应该知道。
     分析3:所以先检查是不是客户端的cookies禁用的情况。于是在环节3的页面加入cookies开启判断的js。发现没有出提示。应该不是客户端偶尔的cookies经用和写入问题。(这里顺便说下,各个浏览器对一个域名下的cookies记录有上限,ie应该是20)
  
    此时在用FF跟踪JSESSIONID时发现登陆后有两个。
    分析4:是否是TOMCAT对多个jsessionId的处理规则导致没有关联到登陆后的那个session。发现两个jsessionId生成的原因在于www.denglish.cn下会产生一个,然后到了www.denglish.cn/exam这个子域也会产生一个。浏览器一般的规则是,子域提交的请求,也会把父域的cookies提交上去。(顺带做了个测试,用js往cookies写入一个和登陆session域一样但是值不一样的jseesionId,tomcat在处理两个jseesionId时还是会自动找到那个session。这里有点不明白了,不管我把手动创造的假jseesionId放在cookies的前面还是后面都是一样的情况,估计tomcat是不是两个都去配置,如果有session返回就返回。这个要看源码认证下)
  
    由于用FF的网络包跟踪看不出问题,在IE上装httpwarcher然后把本机的网速调慢终于发现了问题:首页有一个AJAX请求,是写在了DOMREADY里的,如果网速过慢,会在登录的ACTION提交过后该AJAX请求才提交,并且该AJAX请求居然返回了个新的就JSESSIONID,于是由于域相同,覆盖了COOKIES里对应了登陆信息的JSESSIONID。   对于网速过慢,AJAX请求发出在登录请求之后这个问题解决不了,调为同步也不行。那么分析为什么会有个新的JSESSIONID呢,如果在首页就执行了AJAX请求为什么就没新的JSESSIONID返回?
    为此查找了tomcat建立session的源码。可见我转载的《Tomcat源码---Session的分析》。发现只有在新加了session的时候才会有JSESSIONID写回到cookies
    分析5:由于登录ACTION的代码里是登录验证通过后先把原先的session给invalidate掉然后新建一个session,那么过登录的请求在AJAX请求之前,就会让AJAX里JSESSIONID对应的SESSION失效,所以AJAX会重新建立一个SESSION并返回JSESSIONID。但是AJAX请求没涉及到新建SESSION的操作,为什么会有判断SESSION失效的代码执行呢。
    这里还有个知识点,如果访问JSP页面,么有显示的写入<%@ page session="false"%>那么在编译的SEVLET里会有段代码, session = pageContext.getSession();对于网速查阅的pageContext.getSession()等同于request..getSession()那么这里就会判断session是否存在,并且会在判断没有的时候新建session。  但是当在ajax的返回JSP里加入<%@ page session="false"%>依旧发现其返回了新的jsessionid。
    分析6:还有其它的什么地方再判断SESSION并新增了。
    通过对HttpSessionAttributeListener的继承,跟踪到了新增SESSIOIN的属性,其中发现了name:org.apache.struts.action.LOCALE 和value: zh_CN。这个是struts的国际化自己建立的session。会在.do的请求和通过<forward>标签的url都会执行setLOCAL方法建立session。



    原因都已经找到,如果解决由多种方案,权衡了几种:
    1:首页不用AJAX。其实首页本来就不应该有过多复杂的代码,但是考虑到AJAX的返回JSP会在别的地方有重用,为避免代码不一致的问题,还是保留了首页目前还是用AJAX的方法
    2:取消国际化所新加的SESSION。目前还没找到直接配置国际化开关的选项,而且以后万一在登录后有返回新SESSION的地方还是会出现此问题,首页该方案不可取
    3:修改登录的ACTION,登录成功后保留原先的SESSION。该方法有安全隐患,如果黑客和你公用一个JSESSIONID时,你登陆后的状态会在他刷新后也可以看到。
    4:修改登录的ACTION,登录后新建SESSION,但是原先的SESSION不要invalidate。这样国际化标签不会产生新的SESSION。注意首页不要写<%@ page session="false"%>。应该让其有一个SESSION。这个方案的坏处就是可能会有些无用的SESSION要等到超时才会回收,不过这个问题在客户登陆后直接关掉浏览器也会有,所以影响应该不是很大。

   个人比较倾向于方案4.不过应该也有别的方法来保证JSESSIONID不给覆盖,但是可能会比较复杂点。在此就不深入研究了。最后简略画个流程图来描述问题发生的原因。

访问域名www.denglish.cn :此时建立一个JSESSIONID
   ˇ
自动跳转到首页www.denglish.cn/exam/index.jsp: 由于是子域 所以产生新的JSESSIONID 比如是AAA
   ˇ
   ˇ
   ˇ
index.jsp加载:但是由于网速过慢DOM没完全加载完所以AJAX的请求还没提交,但是会缓存后排队提交
   ˇ
提交登陆的请求:但是此时首页还没加载完,所以AJAX还没提交
   ˇ
登陆请求返回:登陆ACTION中为了安全起见,invalidat掉了旧的session生成了新的session,并回写了新的jsessionid比如是BBB到客户端cookies

AJAX请求提交:国际化标签会过滤请求判断其自己的SESSION是否存在。由于此请求带过去的JSESSIONID 还是AAA,对应的SESSION已经给invalidat掉了,所以产生新的SESSION返回jsessionid比如是CCC,写入客户端COOKIES
   ˇ
点击其它需要获取登陆信息的页面:由于此时提交的jsessionid是CCC,对应的SESSION是国际化的SESSION并非登陆的,所以报错


    问题明了后。虽然是很小的一个细节问题,但是知道了很多与SESSION相关的原理。好记性胜不过烂笔头。还是记录一下。

你可能感兴趣的:(tomcat,jsp,Ajax,struts,IE)