精华内容
下载资源
问答
  • Session超时理解为:浏览器和服务器之间创建了一个Session,由于客户端长时间(休眠时间)没有与服务器交互,服务器将此Session销毁,客户端再一次与服务器交互时之前的Session就不存在了。0.需求需要对所有的/web/**...

    在Java Web开发中,Session为我们提供了很多方便,Session是由浏览器和服务器之间维护的。Session超时理解为:浏览器和服务器之间创建了一个Session,由于客户端长时间(休眠时间)没有与服务器交互,服务器将此Session销毁,客户端再一次与服务器交互时之前的Session就不存在了。

    0.需求

    需要对所有的/web/**请求进行登录拦截,Session超时时跳转到登录页面。

    1.引入

    一般来说,在项目使用中都会配置Session超时时间,如果不配置,则默认值为30分钟,即用户不操作30分钟以后,Session就会失效,此时用户就需要重新登录系统。

    Session超时时间的配置主要的项目的web.xml中进行配置,如下:

    60

    COOKIE

    2.请求的分类

    现在的项目中请求主要分为两种:一种是普通请求,即发起请求返回视图和模型;另外一种是Ajax请求,主要返回模型数据。后端进行处理时就要根据不同的请求返回不同的内容。

    对于普通请求,我们直接返回JavaScript脚本,脚本内容可以是将页面跳转到登录页面。

    对于Ajax请求,则需要返回非200的状态码,这样ajax请求才会进入到error回调函数中以及全局的Ajax错误回调函数AjaxError中。

    3.后端处理Session超时

    后端采用SpringMVC的拦截器处理,这里为什么用拦截器呢?一方面,请求URL不能限制的太死,比如/*,这样对所有的请求都进行过滤是浪费资源的。另一方面,有些URL不需要进行拦截处理,比如到登录页面的请求肯定是不能拦截,要不然会循环重定向。再一方面,我们只需要拦截控制器请求,其它请求不拦截。

    下面看一下拦截器的实现:

    /**

    * Web端登录拦截器

    * 处理请求时Session失效的问题,包含Ajax请求和普通请求

    * @ClassName WebLoginInterceptor

    * @author zhangshun

    * @date 2016年10月20日 上午11:14:52

    */

    public class WebLoginInterceptor extends HandlerInterceptorAdapter{

    /**

    * 日志对象

    */

    private Logger logger = LoggerFactory.getLogger(WebLoginInterceptor.class);

    /**

    * 默认注销URL

    * 即Session超时后,发起请求到此地址,只对普通请求有效

    */

    private static final String DEFAULT_LOGOUT_URL = "/web/logout";

    /**

    * 注销URL

    */

    private String logoutUrl;

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,

    Object handler) throws Exception {

    User user = SessionUtils.getUserFromRequestAcrossCas(request);

    String uri = request.getRequestURI();

    if(user == null){

    response.setContentType("text/html;charset=UTF-8");

    if(request.getHeader("x-requested-with") != null

    && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")){

    // Ajax请求, 前段根据此header进行处理

    response.setHeader("sessionTimeout", "Session time out, you need relogin !");

    // 返回未认证的状态码(401)

    response.setStatus(HttpStatus.UNAUTHORIZED.value());

    logger.debug("请求路径:" + uri + ", 请求方式 :Ajax请求, Session超时, 需要重新登录!");

    }else{

    // 普通请求

    String path = request.getContextPath();

    StringBuffer basePath = new StringBuffer()

    .append(request.getScheme())

    .append("://")

    .append(request.getServerName())

    .append(":")

    .append(request.getServerPort())

    .append(path)

    .append("/");

    StringBuffer responseStr = new StringBuffer()

    .append("

    .append("window.location.href=\"")

    .append(basePath).append(getLogoutUrl()).append("\";")

    .append("");

    response.getWriter().write(responseStr.toString());

    logger.debug("请求路径:" + uri + ",请求方式 :普通请求, Session超时, 需要重新登录!");

    }

    return false;

    }

    return true;

    }

    public String getLogoutUrl() {

    // 使用默认值

    if(StringUtils.isEmpty(logoutUrl)){

    return DEFAULT_LOGOUT_URL;

    }

    return logoutUrl;

    }

    public void setLogoutUrl(String logoutUrl) {

    this

    }

    通过获取Session中的User对象是否存在来判断Session是否超时,如果Session超时,则根据不同的请求方式进行返回。如果是普通请求,则直接返回JavaScript脚本,该脚本可以将页面跳转到其它URL。如果是Ajax请求,则返回401状态码,并且在返回的header中加入sessionTimeout,该数据将会在前端使用。

    该拦截器在SpringMVC配置文件中的配置如下:

    4.前端处理Session超时

    对于普通请求,后端返回的是JavaScript脚本,会立刻执行,这里前端不需要任何处理。

    对于Ajax请求,后端返回401状态码,并在header中设置的sessionTimeout。这里使用jQuery的ajaxComplete回调函数处理,具体如下:

    // 实现ajax请求时判断Session是否失效

    $(document).ajaxComplete(function(event, response, settings) {

    var sessionTimeout = response.getResponseHeader("SessionTimeout");

    if(sessionTimeout != null && typeof sessionTimeout != "undefined" && sessionTimeout.length > 0){

    // 这里写Session超时后的处理方法

    }

    });

    好了,可以了,Session超时的用户都会得到处理。

    总结

    关于Javaweb项目session超时解决方案就到这里,希望对大家有所帮助。

    展开全文
  • session了解及超时处理

    2021-02-12 23:23:24
    Session了解Session是什么引言在web开发中,session是个非常重要的概念。在许多动态网站的开发者看来,session就是一个变量,而且其表现像个黑洞,他只需要将东西在合适的时机放进这个洞里,等需要的时候再把东西取...

    Session了解

    Session是什么

    引言

    在web开发中,session是个非常重要的概念。在许多动态网站的开发者看来,session就是一个变量,而且其表现像个黑洞,他只需要将东西在合适的时机放进这个洞里,等需要的时候再把东西取出来。这是开发者对session最直观的感受,但是黑洞里的景象或者说session内部到底是怎么工作的呢?当笔者向身边的一些同事或朋友问及相关的更进一步的细节时,很多人往往要么含糊其辞要么主观臆断,所谓知其然而不知其所以然。

    笔者由此想到很多开发者,包括我自己,每每都是纠缠于框架甚至二次开发平台之上,而对于其下的核心和基础知之甚少,或者有心无力甚至毫不关心,少了逐本溯源的精神,每忆及此,无不惭愧。曾经实现过一个简单的HttpServer,但当时由于知识储备和时间的问题,没有考虑到session这块,不过近期在工作之余翻看了一些资料,并进行了相关实践,小有所得,本着分享的精神,我将在本文中尽可能全面地将个人对于session的理解展现给读者,同时尽我所能地论及一些相关的知识,以期读者在对session有所了解的同时也能另有所悟,正所谓授人以渔。

    Session是什么

    Session一般译作会话,牛津词典对其的解释是进行某活动连续的一段时间。从不同的层面看待session,它有着类似但不全然相同的含义。比如,在web应用的用户看来,他打开浏览器访问一个电子商务网站,登录、并完成购物直到关闭浏览器,这是一个会话。而在web应用的开发者开来,用户登录时我需要创建一个数据结构以存储用户的登录信息,这个结构也叫做session。因此在谈论session的时候要注意上下文环境。而本文谈论的是一种基于HTTP协议的用以增强web应用能力的机制或者说一种方案,它不是单指某种特定的动态页面技术,而这种能力就是保持状态,也可以称作保持会话。

    为什么需要session

    谈及session一般是在web应用的背景之下,我们知道web应用是基于HTTP协议的,而HTTP协议恰恰是一种无状态协议。也就是说,用户从A页面跳转到B页面会重新发送一次HTTP请求,而服务端在返回响应的时候是无法获知该用户在请求B页面之前做了什么的。

    对于HTTP的无状态性的原因,相关RFC里并没有解释,但联系到HTTP的历史以及应用场景,我们可以推测出一些理由:

    1.  设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。那个时候没有动态页面技术,只有纯粹的静态HTML页面,因此根本不需要协议能保持状态;

    2.  用户在收到响应时,往往要花一些时间来阅读页面,因此如果保持客户端和服务端之间的连接,那么这个连接在大多数的时间里都将是空闲的,这是一种资源的无端浪费。所以HTTP原始的设计是默认短连接,即客户端和服务端完成一次请求和响应之后就断开TCP连接,服务器因此无法预知客户端的下一个动作,它甚至都不知道这个用户会不会再次访问,因此让HTTP协议来维护用户的访问状态也全然没有必要;

    3.  将一部分复杂性转嫁到以HTTP协议为基础的技术之上可以使得HTTP在协议这个层面上显得相对简单,而这种简单也赋予了HTTP更强的扩展能力。事实上,session技术从本质上来讲也是对HTTP协议的一种扩展。

    总而言之,HTTP的无状态是由其历史使命而决定的。但随着网络技术的蓬勃发展,人们再也不满足于死板乏味的静态HTML,他们希望web应用能动起来,于是客户端出现了脚本和DOM技术,HTML里增加了表单,而服务端出现了CGI等等动态技术。

    而正是这种web动态化的需求,给HTTP协议提出了一个难题:一个无状态的协议怎样才能关联两次连续的请求呢?也就是说无状态的协议怎样才能满足有状态的需求呢?

    此时有状态是必然趋势而协议的无状态性也是木已成舟,因此我们需要一些方案来解决这个矛盾,来保持HTTP连接状态,于是出现了cookie和session。

    对于此部分内容,读者或许会有一些疑问,笔者在此先谈两点:

    1.  无状态性和长连接

    可能有人会问,现在被广泛使用的HTTP1.1默认使用长连接,它还是无状态的吗?

    连接方式和有无状态是完全没有关系的两回事。因为状态从某种意义上来讲就是数据,而连接方式只是决定了数据的传输方式,而不能决定数据。长连接是随着计算机性能的提高和网络环境的改善所采取的一种合理的性能上的优化,一般情况下,web服务器会对长连接的数量进行限制,以免资源的过度消耗。

    2.  无状态性和session

    Session是有状态的,而HTTP协议是无状态的,二者是否矛盾呢?

    Session和HTTP协议属于不同层面的事物,后者属于ISO七层模型的最高层应用层,前者不属于后者,前者是具体的动态页面技术来实现的,但同时它又是基于后者的。在下文中笔者会分析Servlet/Jsp技术中的session机制,这会使你对此有更深刻的理解。

    Cookie和Session

    上面提到解决HTTP协议自身无状态的方式有cookie和session。二者都能记录状态,前者是将状态数据保存在客户端,后者则保存在服务端。

    首先看一下cookie的工作原理,这需要有基本的HTTP协议基础。

    cookie是在RFC2109(已废弃,被RFC2965取代)里初次被描述的,每个客户端最多保持三百个cookie,每个域名下最多20个Cookie(实际上一般浏览器现在都比这个多,如Firefox是50个),而每个cookie的大小为最多4K,不过不同的浏览器都有各自的实现。对于cookie的使用,最重要的就是要控制cookie的大小,不要放入无用的信息,也不要放入过多信息。

    无论使用何种服务端技术,只要发送回的HTTP响应中包含如下形式的头,则视为服务器要求设置一个cookie:

    Set-cookie:name=name;expires=date;path=path;domain=domain

    支持cookie的浏览器都会对此作出反应,即创建cookie文件并保存(也可能是内存cookie),用户以后在每次发出请求时,浏览器都要判断当前所有的cookie中有没有没失效(根据expires属性判断)并且匹配了path属性的cookie信息,如果有的话,会以下面的形式加入到请求头中发回服务端:

    Cookie: name="zj"; Path="/linkage"

    服务端的动态脚本会对其进行分析,并做出相应的处理,当然也可以选择直接忽略。

    这里牵扯到一个规范(或协议)与实现的问题,简单来讲就是规范规定了做成什么样子,那么实现就必须依据规范来做,这样才能互相兼容,但是各个实现所使用的方式却不受约束,也可以在实现了规范的基础上超出规范,这就称之为扩展了。无论哪种浏览器,只要想提供cookie的功能,那就必须依照相应的RFC规范来实现。所以这里服务器只管发Set-cookie头域,这也是HTTP协议无状态性的一种体现。

    需要注意的是,出于安全性的考虑,cookie可以被浏览器禁用。

    再看一下session的原理:

    笔者没有找到相关的RFC,因为session本就不是协议层面的事物。它的基本原理是服务端为每一个session维护一份会话信息数据,而客户端和服务端依靠一个全局唯一的标识来访问会话信息数据。用户访问web应用时,服务端程序决定何时创建session,创建session可以概括为三个步骤:

    1.  生成全局唯一标识符(sessionid);

    2.  开辟数据存储空间。一般会在内存中创建相应的数据结构,但这种情况下,系统一旦掉电,所有的会话数据就会丢失,如果是电子商务网站,这种事故会造成严重的后果。不过也可以写到文件里甚至存储在数据库中,这样虽然会增加I/O开销,但session可以实现某种程度的持久化,而且更有利于session的共享;

    3.  将session的全局唯一标示符发送给客户端。

    问题的关键就在服务端如何发送这个session的唯一标识上。联系到HTTP协议,数据无非可以放到请求行、头域或Body里,基于此,一般来说会有两种常用的方式:cookie和URL重写。

    1.  Cookie

    读者应该想到了,对,服务端只要设置Set-cookie头就可以将session的标识符传送到客户端,而客户端此后的每一次请求都会带上这个标识符,由于cookie可以设置失效时间,所以一般包含session信息的cookie会设置失效时间为0,即浏览器进程有效时间。至于浏览器怎么处理这个0,每个浏览器都有自己的方案,但差别都不会太大(一般体现在新建浏览器窗口的时候);

    2.  URL重写

    所谓URL重写,顾名思义就是重写URL。试想,在返回用户请求的页面之前,将页面内所有的URL后面全部以get参数的方式加上session标识符(或者加在path info部分等等),这样用户在收到响应之后,无论点击哪个链接或提交表单,都会在再带上session的标识符,从而就实现了会话的保持。读者可能会觉得这种做法比较麻烦,确实是这样,但是,如果客户端禁用了cookie的话,URL重写将会是首选。

    到这里,读者应该明白我前面为什么说session也算作是对HTTP的一种扩展了吧。如下两幅图是笔者在Firefox的Firebug插件中的截图,可以看到,当我第一次访问index.jsp时,响应头里包含了Set-cookie头,而请求头中没有。当我再次刷新页面时,图二显示在响应中不在有Set-cookie头,而在请求头中却有了Cookie头。注意一下Cookie的名字:jsessionid,顾名思义,就是session的标识符,另外可以看到两幅图中的jsessionid的值是相同的,原因笔者就不再多解释了。另外读者可能在一些网站上见过在最后附加了一段形如jsessionid=xxx的URL,这就是采用URL重写来实现的session。

    584e05abee218abd93fc226bc6d2b50f.png

    (图一,首次请求index.jsp)

    05f1797b7142f7b475d232900dbdcb7a.png

    (图二,再次请求index.jsp)

    Cookie和session由于实现手段不同,因此也各有优缺点和各自的应用场景:

    1.  应用场景

    Cookie的典型应用场景是Remember Me服务,即用户的账户信息通过cookie的形式保存在客户端,当用户再次请求匹配的URL的时候,账户信息会被传送到服务端,交由相应的程序完成自动登录等功能。当然也可以保存一些客户端信息,比如页面布局以及搜索历史等等。

    Session的典型应用场景是用户登录某网站之后,将其登录信息放入session,在以后的每次请求中查询相应的登录信息以确保该用户合法。当然还是有购物车等等经典场景;

    2.  安全性

    cookie将信息保存在客户端,如果不进行加密的话,无疑会暴露一些隐私信息,安全性很差,一般情况下敏感信息是经过加密后存储在cookie中,但很容易就会被窃取。而session只会将信息存储在服务端,如果存储在文件或数据库中,也有被窃取的可能,只是可能性比cookie小了太多。

    Session安全性方面比较突出的是存在会话劫持的问题,这是一种安全威胁,这在下文会进行更详细的说明。总体来讲,session的安全性要高于cookie;

    3.  性能

    Cookie存储在客户端,消耗的是客户端的I/O和内存,而session存储在服务端,消耗的是服务端的资源。但是session对服务器造成的压力比较集中,而cookie很好地分散了资源消耗,就这点来说,cookie是要优于session的;

    4.  时效性

    Cookie可以通过设置有效期使其较长时间内存在于客户端,而session一般只有比较短的有效期(用户主动销毁session或关闭浏览器后引发超时);

    5.  其他

    Cookie的处理在开发中没有session方便。而且cookie在客户端是有数量和大小的限制的,而session的大小却只以硬件为限制,能存储的数据无疑大了太多。

    Servlet/JSP中的Session

    通过上述的讲解,读者应该对session有了一个大体的认识,但是具体到某种动态页面技术,又是怎么实现session的呢?下面笔者将结合session的生命周期(lifecycle),从源代码的层次来具体分析一下在servlet/jsp技术中,session是怎么实现的。代码部分以tomcat6.0.20作为参考。

    创建

    在我问过的一些从事java web开发的人中,对于session的创建时机大都这么回答:当我请求某个页面的时候,session就被创建了。这句话其实很含糊,因为要创建session请求的发送是必不可少的,但是无论何种请求都会创建session吗?错。我们来看一个例子。

    众所周知,jsp技术是servlet技术的反转,在开发阶段,我们看到的是jsp页面,但真正到运行时阶段,jsp页面是会被“翻译”为servlet类来执行的,例如我们有如下jsp页面:

    index.jsp

    This is index.jsp page.

    在我们初次请求该页面后,在对应的work目录可以找到该页面对应的java类,考虑到篇幅的原因,在此只摘录比较重要的一部分,有兴趣的读者可以亲自试一下:

    ......

    response.setContentType("text/html;charset=ISO-8859-1");

    pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true);

    _jspx_page_context = pageContext;

    application = pageContext.getServletContext();

    config = pageContext.getServletConfig();

    session = pageContext.getSession();

    out = pageContext.getOut();

    _jspx_out = out;

    out.write("\r\n");

    out.write(""-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n");

    out.write("\r\n");

    ......

    可以看到有一句显式创建session的语句,它是怎么来的呢?我们再看一下对应的jsp页面,在jsp的page指令中加入了session="true",意思是在该页面启用session,其实作为动态技术,这个参数是默认为true的,这很合理,在此显示写出来只是做一下强调。很显然二者有着必然的联系。笔者在jsp/servlet的翻译器(org.apache.jasper.compiler)的源码中找到了相关证据:

    ......

    if (pageInfo.isSession())

    out.printil("session = pageContext.getSession();");

    out.printil("out = pageContext.getOut();");

    out.printil("_jspx_out = out;");

    ......

    上面的代码片段的意思是如果页面中定义了session="true",就在生成的servlet源码中加入session的获取语句。这只能够说明session创建的条件,显然还不能说明session是如何创建的,本着逐本溯源的精神,我们继续往下探索。

    有过servlet开发经验的应该记得我们是通过HttpServletRequest的getSession方法来获取当前的session对象的:

    public HttpSession getSession(boolean create);

    public HttpSession getSession();

    二者的区别只是无参的getSession将create默认设置为true而已。即:

    public HttpSession getSession() {

    return (getSession(true));

    }

    那么这个参数到底意味着什么呢?通过层层跟踪,笔者终于理清了其中的脉络,由于函数之间的关系比较复杂,如果想更详细地了解内部机制,建议去独立阅读tomcat相关部分的源代码。这里我将其中的大致流程叙述一下:

    1.   用户请求某jsp页面,该页面设置了session="true";

    2.   Servlet/jsp容器将其翻译为servlet,并加载、执行该servlet;

    3.   Servlet/jsp容器在封装HttpServletRequest对象时根据cookie或者url中是否存在jsessionid来决定是绑定当前的session到HttpRequest还是创建新的session对象(在请求解析阶段发现并记录jsessionid,在Request对象创建阶段将session绑定);

    4.   程序按需操作session,存取数据;

    5.   如果是新创建的session,在结果响应时,容器会加入Set-cookie头,以提醒浏览器要保持该会话(或者采用URL重写方式将新的链接呈现给用户)。

    通过上面的叙述读者应该了解了session是何时创建的,这里再从servlet这个层面总结一下:当用户请求的servlet调用了getSession方法时,都会获取session,至于是否创建新的session取决于当前request是否已绑定session。当客户端在请求中加入了jsessionid标识而servlet容器根据此标识查找到了对应的session对象时,会将此session绑定到此次请求的request对象,客户端请求中不带jsessionid或者此jsessionid对应的session已过期失效时,session的绑定无法完成,此时必须创建新的session。同时发送Set-cookie头通知客户端开始保持新的会话。

    保持

    理解了session的创建,就很好理解会话是如何在客户端和服务端之间保持的了。当首次创建了session后,客户端会在后续的请求中将session的标识符带到服务端,服务端程序只要在需要session的时候调用getSession,服务端就可以将对应的session绑定到当前请求,从而实现状态的保持。当然这需要客户端的支持,如果禁用了cookie而又不采用url重写的话,session是无法保持的。

    如果几次请求之间有一个servlet未调用getSession(或者干脆请求一个静态页面)会不会使得会话中断呢?这个不会发生的,因为客户端只会将合法的cookie值传送给服务端,至于服务端拿cookie做什么事它是不会关心的,当然也无法关心。Session建立之后,客户端会一直将session的标识符传送到服务器,无论请求的页面是动态的、静态的,甚至是一副图片。

    销毁

    此处谈到的销毁是指会话的废弃,至于存储会话信息的数据结构是回收被重用还是直接释放内存我们并不关心。Session的销毁有两种情况:超时和手动销毁。

    由于HTTP协议的无状态性,服务端无法得知一个session对象何时将再次被使用,可能用户开启了一个session之后再也没有后续的访问,而且session的保持是需要消耗一定的服务端开销的,因此不可能一味地创建session而不去回收无用的session。这里就引入了一个超时机制。Tomcat中的超时在web.xml里做如下配置:

    30

    上述配置是指session在30分钟没有被再次使用就将其销毁。Tomcat是怎么计算这个30分钟的呢?原来在getSession之后,都要调用它的access方法,修改lastAccessedTime,在销毁session的时候就是判断当前时间和这个lastAccessedTime的差值。

    手动销毁是指直接调用其invalidate方法,此方法实际上是调用expire方法来手动将其设置为超时。

    当用户手动请求了session的销毁时,客户端是无法知道服务端的session已经被销毁的,它依然会发送先前的session标识符到服务端。而此时如果再次请求了某个调用了getSession的servlet,服务端是无法根据先前的session标识符找到相应的session对象的,这是又要重新创建新的session,分配新的标识符,并告知服务端更新session标识符开始保持新的会话。

    Session的数据结构

    在servlet/jsp中,容器是用何种数据结构来存储session相关的变量的呢?我们猜测一下,首先它必须被同步操作,因为在多线程环境下session是线程间共享的,而web服务器一般情况下都是多线程的(为了提高性能还会用到池技术);其次,这个数据结构必须容易操作,最好是传统的键值对的存取方式。

    那么我们先具体到单个session对象,它除了存储自身的相关信息,比如id之外,tomcat的session还提供给程序员一个用以存储其他信息的接口(在类org.apache.catalina.session. StandardSession里):

    public void setAttribute(String name, Object value, boolean notify)

    在这里可以追踪到它到底使用了何种数据:

    protected Map attributes = new ConcurrentHashMap();

    这就很明确了,原来tomcat使用了一个ConcurrentHashMap对象存储数据,这是java的concurrent包里的一个类。它刚好满足了我们所猜测的两点需求:同步与易操作性。

    那么tomcat又是用什么数据结构来存储所有的session对象呢?果然还是ConcurrentHashMap(在管理session的org.apache.catalina.session. ManagerBase类里):

    protected Map sessions = new ConcurrentHashMap();

    具体原因就不必多说了。至于其他web服务器的具体实现也应该考虑到这两点。

    Session Hijack

    Session hijack即会话劫持是一种比较严重的安全威胁,也是一种广泛存在的威胁,在session技术中,客户端和服务端通过传送session的标识符来维护会话,但这个标识符很容易就能被嗅探到,从而被其他人利用,这属于一种中间人攻击。

    本部分通过一个实例来说明何为会话劫持,通过这个实例,读者其实更能理解session的本质。

    首先,我编写了如下页面:

    index.jsp

    This is index.jsp page.

    Object o = session.getAttribute("counter");

    if (o == null) {

    session.setAttribute("counter", 1);

    } else {

    Integer i = Integer.parseInt(o.toString());

    session.setAttribute("counter", i + 1);

    }

    out.println(session.getAttribute("counter"));

    %>

    ">index

    页面的功能是在session中放置一个计数器,第一次访问该页面,这个计数器的值初始化为1,以后每一次访问这个页面计数器都加1。计数器的值会被打印到页面。另外,为了比较简单地模拟,笔者禁用了客户端(采用firefox3.0)的cookie,转而改用URL重写方式,因为直接复制链接要比伪造cookie方便多了。

    下面,打开firefox访问该页面,我们看到了计数器的值为1:

    a76d0c154889df398349b8dc99ef2fdd.png

    (图三)

    然后点击index链接来刷新计数器,注意不要刷新当前页,因为我们没用采用cookie的方式,只能在url后面带上jsessionid,而此时地址栏里的url是无法带上jsessionid的。如图四,我把计数器刷新到了20。

    d99ff720f13be404def374db36604b30.png

    (图四)

    下面是最关键的,复制firefox地址栏里的地址(笔者看到的是http://localhost:8080/sessio

    n/index.jsp;jsessionid=1380D9F60BCE9C30C3A7CBF59454D0A5),然后打开另一个浏览器,此处不必将其cookie禁用。这里我打开了苹果的safari3浏览器,然后将地址粘贴到其地址栏里,回车后如下图:

    73c4ce331916a62efabe906706018dae.png

    (图五)

    很奇怪吧,计数器直接到了21。这个例子笔者是在同一台计算机上做的,不过即使换用两台来做,其结果也是一样的。此时如果交替点击两个浏览器里的index链接你会发现他们其实操纵的是同一个计数器。其实不必惊讶,此处safari盗用了firefox和tomcat之间的维持会话的钥匙,即jsessionid,这属于session hijack的一种。在tomcat看来,safari交给了它一个jsessionid,由于HTTP协议的无状态性,它无法得知这个jsessionid是从firefox那里“劫持”来的,它依然会去查找对应的session,并执行相关计算。而此时firefox也无法得知自己的保持会话已经被“劫持”。

    结语

    到这里,读者应该对session有了更多的更深层次的了解,不过由于笔者的水平以及视野有限,文中也不乏表述欠妥之处,通篇更多地描述了在servlet/jsp中的session机制,但其他开发平台的机制也都万变不离其宗。只要认真思考,你会发现其实这里林林总总之间,总有一些因果关系存在。在软件规模日益增大的背景下,我们更多的时候接触到的是框架、组件,程序员的双眼被蒙蔽了,在这些框架、组件不断产生以及版本的不断更新中,其实有很多相对不变的东西,那就是规范、协议、模式、算法等等,真正令一个人得到提高的还是那些底层的支撑技术。平时多多思考的话,你就能把类似的探索转化为印证。做技术犹如解牛,知筋知骨方能游刃有余。

    session超时,处理ajax请求

    首先建了个拦截器,来判断session超时。用户登录后会保存用户信息在一个session里,在session的监听里,session超时会销毁保存在session里的用户信息,而拦截器就通过session里是否有用户信息来判断session超时。(我总觉得这种方法不怎么好。不知还有什么更好的办法。)

    拦截器是spring-mvc的拦截器,在拦截器里判断是不是ajax请求:

    public boolean preHandle(HttpServletRequest request,

    HttpServletResponse response, Object handler) throws Exception

    {

    if (request.getSession().getAttribute("user") == null)//判断session里是否有用户信息

    {

    if (request.getHeader("x-requested-with") != null

    && request.getHeader("x-requested-with")

    .equalsIgnoreCase("XMLHttpRequest"))//如果是ajax请求响应头会有,x-requested-with;

    {

    response.setHeader("sessionstatus", "timeout");//在响应头设置session状态

    return false;

    }

    }

    return true;

    }

    这样,如果session超时,而且是ajax请求,就会在响应头里,sessionstatus有一个timeout;

    再用一个全局的方法来处理,session超时要跳转的页面。

    jquery 可以用$.ajaxSetup 方法,ext也有类似的方法

    //全局的ajax访问,处理ajax清求时sesion超时

    $.ajaxSetup({

    contentType:"application/x-www-form-urlencoded;charset=utf-8",

    complete:function(XMLHttpRequest,textStatus){

    var sessionstatus=XMLHttpRequest.getResponseHeader("sessionstatus"); //通过XMLHttpRequest取得响应头,sessionstatus,

    if(sessionstatus=="timeout"){

    //如果超时就处理 ,指定要跳转的页面

    window.location.replace("${path}/common/login.do");

    //下面这种方式可以记录之前的请求URI:

    window.location.replace("${path}/common/login.do?rediret=" + enccodeURIComponent(window.location));

    }

    }

    }

    });

    jquery ajax方法调用在session超时以后如何跳转到登录页面

    解决两种情况下的用户访问超时。

    a)普通http请求的session超时。

    b)异步http请求的session超时,使用ext后大部分的界面刷新都是异步的ajax请求。

    不管是那种类型的http请求总是可以由一个过滤器来捕捉。

    分类:普通http请求的header参数中没有x-requested-with:XMLHttpRequest头信息,而异步的有。

    其实对于常见的ajax框架,header中还有标示自己身份的header信息。

    对于普通的http请求,发现session超时后直接重定向到一个超时页面,显示访问超时。

    对于异步http请求,发现session超时后则向请求的response中写入特定的超时头信息,客户端ajax对象检测

    头信息,发现有超时状态标志后调用显示超时信息的javascript方法,提示用户访问超时。

    服务器端session超时后在过滤器中为response添加新的头信息,标记该请求超时:

    if(r.getHeader("x-requested-with")!=null

    && r.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")){

    response.setHeader("sessionstatus","timeout");

    }

    使用Ext.Ajaxt对象完成异步请求的交互,Ext.Ajax是单实例对象(非常重要,全局单一Ext.Ajax实例!)。

    注册Ext.Ajax的requestcomplete事件,每个ajax请求成功后首先响应该事件。在该事件的回调函数里面判断

    访问请求是否超时。使用Ext.Ajax对象的好处是,只需要引入一个包含了几行超时处理代码的js文件,就可以

    为当前应用增加超时处理功能,原有代码不需要做任何修改。

    使用Ext.Ajaxt对象完成异步请求交互,假如checkUserSessionStatus是你的回调方法,每个页面引用:

    Ext.Ajax.on('requestcomplete',checkUserSessionStatus, this);

    function checkUserSessionStatus(conn,response,options){

    //Ext重新封装了response对象

    if(typeof response.getResponseHeader.sessionstatus != 'undefined'){

    //发现请求超时,退出处理代码...

    }

    }

    可以利用的几个特性:a)所有的ajax请求均带有x-requested-with:XMLHttpRequest头信息b)Ext.Ajax是单实例对象(非常重要,全局单一Ext.Ajax实例!)c)注册Ext.Ajax的requestcomplete事件,每个ajax请求成功后首先响应该事件(概念类似spring的aop拦截)。

    jquery提供了几个全局事件可以用来处理session过期请求,如当ajax请求开始时会触发ajaxStart()方法的回调函数;当ajax请求结束时,会触发ajaxStop()方法的回调函数。这些方法都是全局的方法,因此无论创建它们的代码位于何处,只要有ajax请求发生时,都会触发它们。类似的事件还有:ajaxComplete(),ajaxError(),ajaxSend(),ajaxSuccess()等。

    如果使某个ajax请求不受全局方法的影响,那么可以在使用$.ajax()方法时,将参数中的global设置为false,jquery代码如下:$.ajax({ url:"test.html", global:false//不触发全局ajax事件})

    对于其他的ajax框架,解决用户访问请求超时这个问题的思路是类似的。

    java代码:

    当用户session过期时:

    if(request.getHeader("x-requested-with")!=null

    &&request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")){

    PrintWriter printWriter = response.getWriter();

    printWriter.print("{sessionState:0}");

    printWriter.flush();

    printWriter.close();

    }

    js部分的代码:

    /**

    设置jquery的ajax全局请求参数

    */

    $.ajaxSetup({

    contentType:"application/x-www-form-urlencoded;charset=utf-8",

    timeout:pageTimeout,

    cache:false

    ,

    complete:function(XHR,TS){

    var resText=XHR.responseText;

    if(resText=="{sessionState:0}"){

    var nav=judgeNavigator();

    if(nav.indexOf("IE:6")>-1){

    window.opener=null;

    window.close();

    window.open(jsContextPath+'/login.jsp','');

    }else{

    window.open(jsContextPath+'/login.jsp','_top');

    }

    }

    }

    });

    Ext Js 2.2x-requested-with

    js部分

    根目录下建立/js/ext/目录,存放所有和ext相关的js文件。/js/ext/目录下可建立ext相关子目录/js/ext/adapter/ — 存放适配器jquery,prototype,yui。。。/js/ext/experimental/ — 存放ext一些未正式推出的组件,可参考ext开发包examples例子部分。/js/ext/plugins/ — 存放ext扩展组件,例如ext的patch文件,ext主题,扩展组建等等。/js/ext/resources/ — 不用说了,ext开发包中的resources目录直接拷贝。/js/ — 目录下可以放一些最常用 的js文件。/js/ext/ — 目录下放置ext-all.js,ext-base.js,ext-lang-zh_CN.js,ext核心文件;解决两种情况下的用户访问超时。a)普通http请求的session超时。b)异步http请求的session超时,使用ext后大部分的界面刷新都是异步的ajax请求。不管是那种类型的http请求总是可以由一个过滤器来捕捉。分类:普通http请求的header参数中没有x-requested-with:XMLHttpRequest头信息,而异步的有。其实对于常见的ajax框架,header中还有标示自己身份的header信息。对于普通的http请求,发现session超时后直接重定向到一个超时页面,显示访问超时。对于异步http请求,发现session超时后则向请求的response中写入特定的超时头信息,客户端ajax对象检测头信息,发现有超时状态标志后调用显示超时信息的javascript方法,提示用户访问超时。服务器端session超时后在过滤器中为response添加新的头信息,标记该请求超时:Js代码if(r.getHeader("x-requested-with")!=null&& r.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")){response.setHeader("sessionstatus","timeout");}if(r.getHeader("x-requested-with")!=null&& r.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")){response.setHeader("sessionstatus","timeout");}使用Ext.Ajaxt对象完成异步请求的交互,Ext.Ajax是单实例对象(非常重要,全局单一Ext.Ajax实例!)。注册Ext.Ajax的requestcomplete事件,每个ajax请求成功后首先响应该事件。在该事件的回调函数里面判断访问请求是否超时。使用Ext.Ajax对象的好处是,只需要引入一个包含了几行超时处理代码的js文件,就可以为当前应用增加超时处理功能,原有代码不需要做任何修改。使用Ext.Ajaxt对象完成异步请求交互,假如checkUserSessionStatus是你的回调方法,每个页面引用:Js代码Ext.Ajax.on('requestcomplete',checkUserSessionStatus, this);function checkUserSessionStatus(conn,response,options){//Ext重新封装了response对象if(typeof response.getResponseHeader.sessionstatus != 'undefined'){//发现请求超时,退出处理代码...}}Ext.Ajax.on('requestcomplete',checkUserSessionStatus, this);function checkUserSessionStatus(conn,response,options){//Ext重新封装了response对象if(typeof response.getResponseHeader.sessionstatus != 'undefined'){//发现请求超时,退出处理代码...}}可以利用的几个特性:a)所有的ajax请求均带有x-requested-with:XMLHttpRequest头信息b)Ext.Ajax是单实例对象(非常重要,全局单一Ext.Ajax实例!)c)注册Ext.Ajax的requestcomplete事件,每个ajax请求成功后首先响应该事件(概念类似spring的aop拦截)。对于其他的ajax框架,解决用户访问请求超时这个问题的思路是类似的。在这里推荐一个很实用的Js方法:Js代码function getRootWin(){var win = window;while (win != win.parent){win = win.parent;}return win;}function getRootWin(){var win = window;while (win != win.parent){win = win.parent;}return win;}通过该方法,可以在一个任意深度的iframe中调用父iframe中的方法。具体到这里就是无论哪一个iframe中的用户访问请求超时,都可以通过该方法调用最外层iframe中的退出方法,这样便为用户提供了一个统一的访问超时退出的UI呈现。4)系统异常处理将实际业务代码中的各种异常封装成IOException, ServletException异常,指定过滤器捕获。其余处理思路同用户访问超时处理。5)添加jquery支持使用jquery顺手的且希望在Ext项目中同时使用某些jquery插件的时候,添加jquery支持。页面head中直接添加:Js代码6)修改布局常见的布局一般是:header,center,footer,以及一个位于页面左侧的tree menu。其实对于Ext的UI实现来说,去掉header,footer也不错,因为Ext的UI本来就做得挺好看再加上去掉header及footer后可以为center增加不少可视区面积,一个页面还可以显示更多的内容。应该可以支持这两种布局方式的切换,交给用户选择。试了几次,在border布局初始化完毕之后再想去掉header,footer区域好像比较麻烦,ext的官方论坛上也说设计border布局的本意就是应付静态呈现。但是好像已经有javaeye上的同志实现了动态的border布局呵呵。可以参考一下 EXT2的动态BorderLayout组件 。7)更换主题去ext的官网上下载各种主题皮肤 Themes for Ext 2.0主题皮肤文件拷贝至本地/js/ext/plugins/theme/css/,/js/ext/plugins/theme/images/ 目录最好将用户选择的主题配置保存在cookie中,这样用户每次登陆都可以使用相同的界面主题。Ext主题切换:Js代码if($.cookie('ext.theme') != null && $.cookie('ext.theme') != 'default'){Ext.util.CSS.swapStyleSheet("theme","/js/ext/plugins/theme/css/"+$.cookie('ext.theme'));}if($.cookie('ext.theme') != null && $.cookie('ext.theme') != 'default'){Ext.util.CSS.swapStyleSheet("theme","/js/ext/plugins/theme/css/"+$.cookie('ext.theme'));}8)添加自定义的toolbar图标直接参考javaeye上的这边文章 共享一些Ext的图标 即可,作者提供的图标很好看,使用也非常简单。9)生成Excel文档最先参考的资料是extjs论坛上面的这篇文章:GridPanel directly to Excel.作者思路不错,就是利用javascript直接读取GridPanel的store数据,然后生成一个描述excel文档的xml数据,最后再通过一个包含了该xml数据的"data" URL下载该excel。该方法的好处是通用性比较强,生成的excel文档也不难看,并且是不需要服务器端参与处理的一种纯客户端解决方案。但是最大的缺点是目前IE7不支持(This needs a browser that supports data URLs. FF, Opera and IE8 will support this.)。而后发现dojochina网站上的一个用户整理和修改了这个生成excel文档的实现方法。引用以下的几个问题我都已经整理和修改:1、没有考虑到含有序号和选择框的grid,2、utf8转换bug.3、宽度的bug4、不支持ie6、ie7和Safari原文地址:官方Grid导出到Excel修正版 (作者给出的代码有些小问题,需要略微进行些调整)如果是IE浏览器,客户端将以multipart/form-data方式向服务器端提交该xml数据。原文给出了后台由php实现时的exportexcel.php代码。如果后台由java实现,exportexcel.jspJava代码1000)break;if(part.isParam() && part.getName().equalsIgnoreCase("exportContent")){param = (ParamPart)part;break;}maxcount++;}if(param!=null){response.getWriter().println(param.getStringValue());}else{;}%>1000)break;if(part.isParam() && part.getName().equalsIgnoreCase("exportContent")){param = (ParamPart)part;break;}maxcount++;}if(param!=null){response.getWriter().println(param.getStringValue());}else{;}%>这里使用 com.oreilly.servlet 解析multipart/form-data类型数据。com.oreilly.servlet 很适合文件,表单混合提交、多文件上传的数据解析。10)js文件管理凡是这种基于javascript的富客户端解决方案一大问题就是js文件太多。每个页面不仅要导入Ext的css,js文件,还要导入每个页面应用需要的一些js文件,这样管理起来很麻烦。原来的情况,至少要导入:Html代码推荐使用 JSLoader 管理众多的js,css文件1,编写一个js文件统一管理支持所有公用css,js文件的动态导入Js代码//添加jquery支持JSLoader.loadJavaScript("/js/ext/adapter/jquery/jquery.js");JSLoader.loadJavaScript("/js/jquery.cookie.js");JSLoader.loadJavaScript("/js/ext/adapter/jquery/ext-jquery-adapter.js");//Ext支持JSLoader.loadStyleSheet("/js/ext/resources/css/ext-all.css");JSLoader.loadJavaScript("/js/ext/ext-base.js");JSLoader.loadJavaScript("/js/ext/ext-all.js");JSLoader.loadJavaScript("/js/ext/ext-lang-zh_CN.js");//加载自定义toolbar图标css样式JSLoader.loadStyleSheet("/js/ext/plugins/icon/css/ext-extend.css");//加载用户超时,异常处理JSLoader.loadJavaScript("/js/extajax.js");//主题管理JSLoader.loadJavaScript("/js/exttheme.js");//Excel导出支持JSLoader.loadJavaScript("/js/ext.excel.js");//添加jquery支持JSLoader.loadJavaScript("/js/ext/adapter/jquery/jquery.js");JSLoader.loadJavaScript("/js/jquery.cookie.js");JSLoader.loadJavaScript("/js/ext/adapter/jquery/ext-jquery-adapter.js");//Ext支持JSLoader.loadStyleSheet("/js/ext/resources/css/ext-all.css");JSLoader.loadJavaScript("/js/ext/ext-base.js");JSLoader.loadJavaScript("/js/ext/ext-all.js");JSLoader.loadJavaScript("/js/ext/ext-lang-zh_CN.js");//加载自定义toolbar图标css样式JSLoader.loadStyleSheet("/js/ext/plugins/icon/css/ext-extend.css");//加载用户超时,异常处理JSLoader.loadJavaScript("/js/extajax.js");//主题管理JSLoader.loadJavaScript("/js/exttheme.js");//Excel导出支持JSLoader.loadJavaScript("/js/ext.excel.js");2,每个页面只需要引入:Html代码

    展开全文
  • 这篇文章主要介绍了Javaweb项目session超时解决方案,关于解决方案分类比较明确,内容详细,需要的朋友可以参考下。在Java Web开发中,Session为我们提供了很多方便,Session是由浏览器和服务器之间维护的。Session...

    这篇文章主要介绍了Javaweb项目session超时解决方案,关于解决方案分类比较明确,内容详细,需要的朋友可以参考下。

    在Java Web开发中,Session为我们提供了很多方便,Session是由浏览器和服务器之间维护的。Session超时理解为:浏览器和服务器之间创建了一个Session,由于客户端长时间(休眠时间)没有与服务器交互,服务器将此Session销毁,客户端再一次与服务器交互时之前的Session就不存在了。

    0.需求

    需要对所有的/web/**请求进行登录拦截,Session超时时跳转到登录页面。

    1.引入

    一般来说,在项目使用中都会配置Session超时时间,如果不配置,则默认值为30分钟,即用户不操作30分钟以后,Session就会失效,此时用户就需要重新登录系统。

    Session超时时间的配置主要的项目的web.xml中进行配置,如下:

    60

    COOKIE

    2.请求的分类

    现在的项目中请求主要分为两种:一种是普通请求,即发起请求返回视图和模型;另外一种是Ajax请求,主要返回模型数据。后端进行处理时就要根据不同的请求返回不同的内容。

    对于普通请求,我们直接返回JavaScript脚本,脚本内容可以是将页面跳转到登录页面。

    对于Ajax请求,则需要返回非200的状态码,这样ajax请求才会进入到error回调函数中以及全局的Ajax错误回调函数AjaxError中。

    3.后端处理Session超时

    后端采用SpringMVC的拦截器处理,这里为什么用拦截器呢?一方面,请求URL不能限制的太死,比如/*,这样对所有的请求都进行过滤是浪费资源的。另一方面,有些URL不需要进行拦截处理,比如到登录页面的请求肯定是不能拦截,要不然会循环重定向。再一方面,我们只需要拦截控制器请求,其它请求不拦截。

    下面看一下拦截器的实现:

    /**

    * Web端登录拦截器

    * 处理请求时Session失效的问题,包含Ajax请求和普通请求

    * @ClassName WebLoginInterceptor

    * @author zhangshun

    * @date 2016年10月20日 上午11:14:52

    */

    public class WebLoginInterceptor extends HandlerInterceptorAdapter{

    /**

    * 日志对象

    */

    private Logger logger = LoggerFactory.getLogger(WebLoginInterceptor.class);

    /**

    * 默认注销URL

    * 即Session超时后,发起请求到此地址,只对普通请求有效

    */

    private static final String DEFAULT_LOGOUT_URL = "/web/logout";

    /**

    * 注销URL

    */

    private String logoutUrl;

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,

    Object handler) throws Exception {

    User user = SessionUtils.getUserFromRequestAcrossCas(request);

    String uri = request.getRequestURI();

    if(user == null){

    response.setContentType("text/html;charset=UTF-8");

    if(request.getHeader("x-requested-with") != null

    && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")){

    // Ajax请求, 前段根据此header进行处理

    response.setHeader("sessionTimeout", "Session time out, you need relogin !");

    // 返回未认证的状态码(401)

    response.setStatus(HttpStatus.UNAUTHORIZED.value());

    logger.debug("请求路径:" + uri + ", 请求方式 :Ajax请求, Session超时, 需要重新登录!");

    }else{

    // 普通请求

    String path = request.getContextPath();

    StringBuffer basePath = new StringBuffer()

    .append(request.getScheme())

    .append("://")

    .append(request.getServerName())

    .append(":")

    .append(request.getServerPort())

    .append(path)

    .append("/");

    StringBuffer responseStr = new StringBuffer()

    .append("

    .append("window.location.href=\"")

    .append(basePath).append(getLogoutUrl()).append("\";")

    .append("");

    response.getWriter().write(responseStr.toString());

    logger.debug("请求路径:" + uri + ",请求方式 :普通请求, Session超时, 需要重新登录!");

    }

    return false;

    }

    return true;

    }

    public String getLogoutUrl() {

    // 使用默认值

    if(StringUtils.isEmpty(logoutUrl)){

    return DEFAULT_LOGOUT_URL;

    }

    return logoutUrl;

    }

    public void setLogoutUrl(String logoutUrl) {

    this

    }

    通过获取Session中的User对象是否存在来判断Session是否超时,如果Session超时,则根据不同的请求方式进行返回。如果是普通请求,则直接返回JavaScript脚本,该脚本可以将页面跳转到其它URL。如果是Ajax请求,则返回401状态码,并且在返回的header中加入sessionTimeout,该数据将会在前端使用。

    该拦截器在SpringMVC配置文件中的配置如下:

    4.前端处理Session超时

    对于普通请求,后端返回的是JavaScript脚本,会立刻执行,这里前端不需要任何处理。

    对于Ajax请求,后端返回401状态码,并在header中设置的sessionTimeout。这里使用jQuery的ajaxComplete回调函数处理,具体如下:

    // 实现ajax请求时判断Session是否失效

    $(document).ajaxComplete(function(event, response, settings) {

    var sessionTimeout = response.getResponseHeader("SessionTimeout");

    if(sessionTimeout != null && typeof sessionTimeout != "undefined" && sessionTimeout.length > 0){

    // 这里写Session超时后的处理方法

    }

    });

    好了,可以了,Session超时的用户都会得到处理。

    总结

    展开全文
  • 环境:springboot+shiro+jquery-easyui问题:在ajax请求时,如果此时session已经失效,系统没有自动跳转到登录页面。后来在服务端加了判断ajax请求的代码,结果还是没有用,无法取到ajax特定的head值(X-Requested-...

    本问题解决方案参照网站多篇文章融合解决,在此表示感谢!

    环境:springboot+shiro+jquery-easyui

    问题:在ajax请求时,如果此时session已经失效,系统没有自动跳转到登录页面。后来在服务端加了判断ajax请求的代码,结果还是没有用,无法取到ajax特定的head值(X-Requested-With)。发现jquery-easyui表单提交时没有就没有传递这个值。

    解决办法:

    1.添加拦截器

    import javax.servlet.ServletRequest;

    import javax.servlet.ServletResponse;

    import javax.servlet.http.HttpServletRequest;

    import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

    import org.slf4j.Logger;

    import org.slf4j.LoggerFactory;

    public class SessionFilter extends FormAuthenticationFilter {

    private Logger logger = LoggerFactory.getLogger(SessionFilter.class);

    private final static String X_REQUESTED_WITH_STRING = "X-Requested-With";

    private final static String XML_HTTP_REQUEST_STRING = "XMLHttpRequest";

    private final static String SESSION_OUT_STIRNG = "sessionOut";

    @Override

    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {

    if (this.isLoginRequest(servletRequest, servletResponse)) {

    if (this.isLoginSubmission(servletRequest, servletResponse)) {

    return this.executeLogin(servletRequest, servletResponse);

    } else {

    return true;

    }

    } else {

    if (isAjax((HttpServletRequest) servletRequest)) {

    servletResponse.getWriter().print(SESSION_OUT_STIRNG);

    } else {

    this.saveRequestAndRedirectToLogin(servletRequest, servletResponse);

    }

    return false;

    }

    }

    public boolean isAjax(HttpServletRequest httpServletRequest) {

    String header = httpServletRequest.getHeader(X_REQUESTED_WITH_STRING);

    if (XML_HTTP_REQUEST_STRING.equalsIgnoreCase(header)) {

    logger.debug("当前请求为Ajax请求:{}", httpServletRequest.getRequestURI());

    return Boolean.TRUE;

    }

    logger.debug("当前请求非Ajax请求:{}", httpServletRequest.getRequestURI());

    return Boolean.FALSE;

    }

    }

    2.覆盖默认shiro拦截器

    @Bean

    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {

    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

    // 必须设置 SecurityManager

    shiroFilterFactoryBean.setSecurityManager(securityManager);

    // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面

    shiroFilterFactoryBean.setLoginUrl("/login");

    // 登录成功后要跳转的链接

    shiroFilterFactoryBean.setSuccessUrl("/index");

    // 未授权界面;

    shiroFilterFactoryBean.setUnauthorizedUrl("/403");

    // 自定义拦截器

    Map filtersMap = new LinkedHashMap();

    // 限制同一帐号同时在线的个数。

    filtersMap.put("kickout", filterKickoutSessionControl());

    shiroFilterFactoryBean.setFilters(filtersMap);

    // 权限控制map.

    Map filterChainDefinitionMap = new LinkedHashMap();

    filterChainDefinitionMap.put("/servlet/authimage", "anon");

    filterChainDefinitionMap.put("/**", "authc");

    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

    Map filters=new LinkedHashMap<>();

    filters.put("authc", new SessionFilter());

    shiroFilterFactoryBean.setFilters(filters);

    return shiroFilterFactoryBean;

    }

    这个是重点,覆盖shiro默认的拦截器:

    Map filters=new LinkedHashMap<>();

    filters.put("authc", new SessionFilter());

    shiroFilterFactoryBean.setFilters(filters);

    3.改造前端

    一开始采用了修改form提交的参数,把iframe=false就可以提交X-Requested-With,但是这样的异步请求实在太多,不仅仅在form提交,还有combo,tree等等。所以最后用了网上说的方法,改造$.ajax达到统一处理。

    //首先备份下jquery的ajax方法

    var _ajax = $.ajax;

    // 重写jquery的ajax方法

    $.ajax = function(opt) {

    // 备份opt中error和success方法

    var fn = {

    error : function(XMLHttpRequest, textStatus, errorThrown) {

    },

    success : function(data, textStatus) {

    }

    }

    if (opt.error) {

    fn.error = opt.error;

    }

    if (opt.success) {

    fn.success = opt.success;

    }

    // 扩展增强处理

    var _opt = $.extend(opt, {

    error : function(XMLHttpRequest, textStatus, errorThrown) {

    debugger;

    erro = eval("(" + XMLHttpRequest.responseText + ")");

    // 错误方法增强处理

    fn.error(XMLHttpRequest, textStatus, errorThrown);

    },

    success : function(data, textStatus) {

    if (data != 'sessionOut') {

    fn.success(data, textStatus)

    return false;

    } else {

    top.location.href = appPath + "/";

    }

    }

    });

    return _ajax(_opt);

    };

    这个地方要根据个人情况修改一下:

    06ef5f140768b0950f55de0502cb8b1b.png

    ajax提交session超时跳转页面使用全局的方法来处理

    来自:http://www.jb51.net/article/43770.htm 如果是ajax提交,超时时从服务器发出的跳转命令就不会起作用,所以如果是session超时,而且是在ajax请求,就在 ...

    Ajax请求Session超时的解决办法:拦截器 &plus; 封装jquery的post方法

    目标:前端系统,后端系统等,统一处理Session超时和系统错误的问题. 可能需要处理的问题:Session超时.系统500错误.普通的业务错误.权限不足. 同步请求:            Sess ...

    Ajax请求Session超时解决

    web前端js代码: $.ajaxSetup({ contentType : "application/x-www-form-urlencoded;charset=utf-8", ...

    Shiro中session超时页面跳转的处理

    问题描述 shiro在管理session后,在session超时会进行跳转,这里有两种情况需要考虑,一种是ajax方式的请求超时,一种页面跳转请求的超时. 本文从这两个方面分别考虑并处理. ajax请 ...

    java Servlet Filter 拦截Ajax请求,统一处理session超时的问题

    后台增加filter,注意不要把druid也屏蔽了 import java.io.IOException; import javax.servlet.Filter; import javax.serv ...

    spring security:ajax请求的session超时处理

    当前端在用ajax请求时,如果没有设置session超时时间并且做跳转到登录界面的处理,那么只是靠后台是很难完成超时的一系列动作的:但是如果后台 没有封装一个ajax请求公共类,那么在ajax请求上下 ...

    解决spring-security session超时 Ajax 请求没有重定向的问题

    开始时, 代码是这样的: $.ajax({ type : "POST", url : sSource, cache : false, dataType : "json&q ...

    dwz ajax session超时跳转登录页(struts2自定义拦截器)

    1.定义struts2拦截器(网上例子很多) 代码如下: package rt.intercepter; import java.util.Map; import javax.servlet.http ...

    Ajax 提交session实效跳转到完整的登陆页面

    在spring security 中 Ajax提交时,session超时,局部加载登陆页面,为解决这个问题,重写ajax提交,返回的是modeview或者没有制定datatype时; 如果检测到加载到 ...

    随机推荐

    Java &plus; Excel 接口自动化

    最近项目比较悠闲,想找点事干,写了个 Excel 接口测试的 "框架" 以前用 python 写过一个,这次用 java, 应该说框架都不算,反正就是写了,能帮我解决问题就行. 当 ...

    iOS-OC-基础-NSDictionary常用方法

    /*=============================NSDictionary(不可变字典)的使用=========================*/ //————————————————— ...

    accordion

    展开全文
  • Javaweb项目中session出现超时如何解决发布时间:2020-12-05 17:34:03来源:亿速云阅读:98作者:LeahJavaweb项目中session出现超时如何解决?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家...
  • 一、什么是Session Cookie技术可以将用户的信息保存在各自的浏览器中,并且可以在多次请求下实现数据的共享。但是,如果传递的信息比较多,使用Cookie技术显然会增大服务器端程序处理难度,这时可以使用Session技术...
  • 目标:前端系统,后端系统等,统一处理Session超时和系统错误的问题。可能需要处理的问题:Session超时、系统500错误、普通的业务错误、权限不足。同步请求:Session超时,登录拦截器,重定向到登录页面。500等系统...
  • php session超时设置

    2021-03-24 09:44:58
    来自http://nickandmiles.blog.163.com/blog/static/2342212320115204621410/ 修改php中的session过期时间可以修改php配置文件php.ini中的session.gc_maxlifetime即可。...如果"session回收"发生,那...
  • php的session超时问题

    2021-04-12 13:41:29
    session通常是根据php.ini中的 session.gc_maxlifetimie 的来控制超时时间,gc回收机制是有概率的,也就是说超时过期后不一定会清掉sessiongc回收概率是通过php.ini中的session.gc_probability和sesssion.gc_divisor...
  • 这个值以分钟为单位,并覆盖weblogic.xml中的TimeoutSecs属性60此例表示Session将在60分钟后过期当设置为-2,表示将使用在weblogic.xml中设置的TimeoutSecs这个属性值。当设置为-1,表示Session将永不过期,而忽略...
  • 原来IIS为了保护服务器,有一个“回收”的概念!测试了半天终于有了点大体了解(不要笑我菜^-^)。先来看看这个“回收”在哪设置。启动IIS管理器->...我个人理解:在session.timeout之后再过1740分钟自动把所有...
  • hp默认的会话处理器是以文件形式存储在服务器端的,为保证session文件中数据的安全,每次只能有一个请求去操作session文件,这个时候session文件就会被加上锁,只能等到脚本执行结束或者session正常关闭才会解锁。
  • 今天本人处理了一起springboot项目生产session-out超时问题,项目采用下面方案四application.yaml配置session-out。先描述下问题,在测试环境通过改动application.yaml配置session-out,经过设置不同值发现session-...
  • JS实现页面超时后自动跳转到登陆页面网友方案:var myTime = setTimeout("Timeout()", 60000);function resetTime() {clearTimeout(myTime);...}function Timeout() {alert("您的登录已超时, 请点确定后重新登录!");...
  • 原来IIS为了保护服务器,有一个“回收”的概念!测试了半天终于有了点大体了解(不要笑我菜^-^)。先来看看这个“回收”在哪设置。启动IIS管理器->...我个人理解:在session.timeout之后再过1740分钟自动把所有...
  • 后来发现session超时,前端未能接收到相应超时信息; 经查看发现,之前类继承的是AccessControlFilter ,然后改为继承FormAuthenticationFilter: 直接上代码: import com.tzwy.mcsp.response.BaseResponse; import...
  • 如何实现不刷新页面时的session超时控制当有提交请求处理的时候,可以用拦截器或者过滤器实现超时监听我现在想实现,页面无刷新,使用js计数器控制页面超时每次鼠标单击都重新赋值计时器为0若长时间没有点击鼠标,...
  • 内嵌页面session超时,内嵌页面显示登录界面问题解决方案我们在开发web项目的时候 经常用到iframe嵌套,这里的话 不管是开发的时候,还是正式环境使用。当session超时,或者重启项目,内嵌的页面会跳转到登录页面;...
  • 1、session原理cookie是保存在用户浏览器端的键值对session是保存在服务器端的键值对session服务端中存在的数据为: session = {随机字符串1:{用户1的相关信息}随机字符串2:{用户2的相关信息}} session客户端即...
  • ​ 当设置的HttpSession时间超时时,系统就会销毁用户登录存储的session信息,但此时再打开一个浏览器登录就会报错,是因为此时,ServletContext中存储的用户session还在,再次登录销毁另一个已经不存在的session就...
  • php登录超时session解决办法:首先登录时候用session记录登录时间;然后页面打开时候判断session如果不存在,就跳回登录页面;接着如果session存在,则将页面加载时间和登录时间对比;最后如果大于超时时间,则...
  • 在Web应用中,会用到大量的Ajax请求,在Ajax请求中存在的一个问题就是如何判断session超时,在网上找到的一个java中处理此问题方法:1.java中:public booleanpreHandle(HttpServletRequest request,...
  • Servlet会话超时(Servlet Session timeout)我正在写我的SPring MVC网络应用程序。我已将会话时间设置为10080分钟,等于1周。 现在我想让用户在每次打开浏览器时都登录:sessionService.setcurrentUser(myuser);...
  • win2003 server下的IIS6默认设置下对每个运行在默认应用池中的工作者进程都会经过20多个小时后自动...解决办法:修改配置,设置为不定时自动回收该工作者进程,比如设置为当超出占用现有物理内存60%后自动回收该进程...
  • 当访问终端在windows上时,各终端软件,如,putty,SecureCRT等,大多支持设置向服务器端自动发送消息,来防止终端定期超时。其实,服务器端也支持类似的设置,从服务器的角度防止链接超时。并且,当终端在Centos等...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 76,915
精华内容 30,766
关键字:

session超时怎么解决