`

一次刷新重复提交问题及其引入的新问题的解决过程记录

阅读更多

一次刷新重复提交问题及其引入的新问题的解决过程记录

 

1. 问题描述

 

   用户登陆后,刷新页面,IE提示重复提交,需要用户做出选择,影响用户体验.

  

2. 问题分析.

   

   登陆页面是一个jsp,form中使用一个loginAction.action提交登陆.提交后在struts中

   dispatch到主页面,即index.jsp,此时浏览器地址栏显示地址是用于登陆的action,

   即: http://ip:port/loginAction.action这样的格式.

   

   这个action地址就是上面登陆页面form的action,这样一按F5刷新,

   造成重复提交(即,重复登陆).

   

3. 问题解决

 

   登陆成功,将用户ID压入session后,让struts的action dispatch到一个临时的jsp,

   这个jsp打开后,配置header的META,自动刷新到index.jsp页面.

   <META HTTP-EQUIV="Refresh" CONTENT="0;URL=<%=request.getContextPath()%>/index.jsp"> 

   这样登陆后,浏览器中显示http://ip:port/index.jsp.

   这样刷新时,就不会产生重复提交问题.

   

4. 引入新问题

 

   上面的http://ip:port/index.jsp页面刷新后,发现又跳到登陆界面了.

   跟踪发现,页面一刷新,登陆时压入session的用户id变成null,

   

   导致用户重复处于未登陆状态. 就是刷新导致session丢失.

   

   google发现,IE7中,当刷新一个带有iframe的页面时(index.jsp确实有一个frameset,包括leftFrame和mainFrame),

   会导致session丢失. 

   

   很不幸,虽然测试使用的浏览器是IE8,当时也以为是这个刷新问题,折腾了不少时间,无果.

   

   后来,无意跟踪过滤器代码发现,每次刷新或者关闭页面时,都会请求一个logout.action.

   

   logout.action这个logout专门注销session.坑爹啊.....

   

   检查index.jsp代码,发现注册了window.onbeforeunload事件,在这个事件中,请求logout.action.

   

   于是导致,页面一刷新或者关闭页面所在的浏览器选项卡,或者直接关闭浏览器,

   都会触发这个onbeforeunload事件,然后用户就logout了.

   

   配置这个onbeforeunload事件,是因为,一个用户不能在不同的地方同时登陆系统.

   

   这样,当一个用户用一个账号登陆时,这个账号就不能在别处登陆.这样就要求一旦用户从一个地方退出登陆,

   或者关闭浏览器就要实现自动注销.否则,一个用户已经关闭浏览器退出登陆了,

   该账号在session过期时间之内还是不能在别的地方登陆.于是注册onbeforeunload事件,当用了关闭浏览器,

   就会自动登出.

   

   但是注册onbeforeunload事件,在刷新时也会触发.

   

   于是,产生了新问题,不能在onbeforeunload事件中让用户退出登陆,而且当用户长时间不操作,或者关闭

   浏览器后,用户能自动退出登陆.因为注销session后,用户就失效,这跟让session过期是一样的.

   于是想到了解决方案.

   

5. 引入问题解决

 

   在index.jsp页面设置一个session超时时间sessionTimeOut,设置为10分钟.

   然后启动一个定时器(setInterval),每10秒钟执行一次,让sessionTimeOut减10.

   

   同时,在这个定时器(setInterval)中,检查sessionTimeOut,假如sessionTimeOut>0,

   就发送ajax请求(心跳),每次将当前session过期时间设置为15秒以后.

   

   另外,在index.jsp的主页面和两个frame页面都注册document的mousemove事件,只要一检测

   到鼠标移动(说明在操作),就把sessionTimeOut重新设置为初始值10分钟.

   

   因为,两个frame中的页面随时都可能变化,这里就需要在设置一个定时器,每隔一秒钟给两个frame

   中的页面的document注册mousemove事件,以重设sessionTimeOut.

   

   这样,当长期不操作系统,sessionTimeOut减小为0后,停止发送ajax请求(心跳),则15秒后,session过期,

   导致用户退出登陆.

   

   index.jsp中js代码(使用jquery)

  var SESSION_TIME = 10 * 60 * 1000; //十分钟

var setChangeIterval = 1000; 

  var setHeartBeatInterval = 10 * 1000; // 不能配置大于15秒,后台写死了

  var sessionTimeOut = SESSION_TIME; //页面没有做操作时间的 超过10分钟 + 15秒(最多),则session过期,需要重新登陆

  

  //鼠标移动事件

  function docMouseMoveFunc()

  {

    //鼠标不动时,该变量每隔10秒钟减少10,当该变量变成0后,页面不发送心跳,这样当前session最多在15秒后过期.

    //同时,给页面绑定mouse事件,鼠标移动,改变量值恢复为初始值.

    sessionTimeOut = SESSION_TIME;

  }

  

  //心跳函数,每10秒钟执行一次,

  function hearBeat()

  {

    if(sessionTimeOut > 0)

    {

      var url = "<%=request.getContextPath()%>/heartBeat.action"; 

 

 $.ajax({

     url:url, 

     cache: false,             

     success:function(data, textStatus)

     { 

       if(data.heartBeatDone != 1) //说明后台报错,则不再发送心跳请求

       {

         sessionTimeOut = 0;

       }

     },

     type:'post',

     async:true,

     dataType:'json',

     error:function(XMLHttpRequest, textStatus, errorThrown)

     {

     }

 });

      

      sessionTimeOut = sessionTimeOut - 10 * 1000;

    }

    

  }

  

  //给框架页面绑定事件,为防止框架中页面改变,该函数每隔一秒执行一次

  function frameDocChange()

  {

    $(frames.leftFrame.document).mousemove(docMouseMoveFunc);

    

    $(frames.mainFrame.document).mousemove(docMouseMoveFunc);

  }

  

  //给框架页面绑定事件,为防止框架中页面改变,该函数每隔一秒执行一次

  setInterval('frameDocChange()',setChangeIterval);

  

  //每隔10秒钟执行一次心跳,将当前session过期时间设置为15秒之后

  setInterval('hearBeat()',setHeartBeatInterval);

 

//

$( document ).mousemove(docMouseMoveFunc);

  

  //页面加载完成后执行一次心跳

  $(document).ready(

  function()

  {

    hearBeat();

    sessionTimeOut = SESSION_TIME; //恢复超时计时

  });

  -------------------------------

  action配置:

  <action name="heartBeat" class="xxx.HeartBeatAction" method="heartBeat">

    <result name="sucess">/heartBeatResult.jsp</result>

  </action>

  

  heartBeat方法:

  public String heartBeat()

  {

    heartBeatDone = "1";

    try

    {

      ctx = ActionContext.getContext();

      HttpServletRequest request = ServletActionContext.getRequest();

      HttpSession httpSession = request.getSession();

      httpSession.setMaxInactiveInterval(15); //前台每隔10秒将当前session过期时间设置为15秒以后

      System.out.println(new Date());

      

    }

    catch (Exception e)

    {

      heartBeatDone = "0";

    }

    

    return "sucess";

  }

  

  heartBeatResult.jsp代码:

  <%@page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>

  <%@taglib prefix="s" uri="/struts-tags"%>

  { heartBeatDone:${heartBeatDone}}

   

   

   

   

   

 

    

    

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics