控制是否更新会话时间戳

时间:2013-10-04 22:18:48

标签: java spring tomcat

在我的Tomcat 7.0上使用Spring Web MVC运行的应用程序中,我有一些控制器,尽管对它们的请求需要身份验证和有效会话,但我不希望更新会话的到期时间戳。换句话说,我希望会话准确到期,否则会发生此特定HTTP请求。

这些是AJAX方法,如果它很重要,虽然我不知道它是否。

可以通过通用Java EE或一些特殊的Tomcat挂钩来完成吗?还有另一种方法来实现这一目标吗?我知道http://download.oracle.com/javaee/6/api/javax/servlet/http/HttpSession.html#setMaxInactiveInterval%28int%29,但这似乎与我想要的完全相反。

2 个答案:

答案 0 :(得分:2)

当会话为being accessed by HttpServletRequest#getSession时,

访问时间仅更新为 。因此,如果您确定没有篡改会话,那么您应该没问题。 更新:基于the JavaDoc上述情况并非如此(即使您搜索了源代码代码彻底,并没有发现任何代码负责此类行为。)

另一方面,如果你需要在AJAX请求中访问会话,那么你几乎搞砸了。我能想到的唯一解决方案是手动存储lastAccessTime(例如在servlet过滤器中),然后检查会话超时和invalidate the session manually(例如在同一过滤器中)。这应该非常简单易行。


更新:为了实现过滤器的乐趣(未经过测试):

public class SessionInvalidationFilter implements Filter {

    private static final String LAST_ACCESS_SESSION_ATTR = "lastAccessTime";

    private static final long SESSION_TIMEOUT = 1000 * 60 * 20; // 20 minutes

    private static final String IGNORE_ACCESS_URI = "/this/will-not/update/access-time";

    @Override
    public void init(FilterConfig arg0) throws ServletException {
    }

    @Override
    public void destroy() {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // Cast to HTTP request and response
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        // Check if we are handling standard request
        if (!IGNORE_ACCESS_URI.equals(httpRequest.getRequestURI())) {
            chain.doFilter(new SessionAccessAwareRequest(httpRequest), response);
            return;
        }
        // Now we can handle the special case of non-tracked request
        boolean expired = false;
        HttpSession session = httpRequest.getSession(false);
        if (session == null) {
            // No session means the AJAX contained no or incorrect JSESSIONID
            expired = true;
        } else {
            Long lastAccessTime = (Long) session.getAttribute(LAST_ACCESS_SESSION_ATTR);
            if (lastAccessTime == null || lastAccessTime + SESSION_TIMEOUT < System.currentTimeMillis()) {
                session.invalidate(); // Invalidate manually
                expired = true;
            }
        }
        // Handle error or process normally
        if (expired) {
            httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST);
        } else {
            chain.doFilter(request, response);
        }
    }

    private static class SessionAccessAwareRequest extends HttpServletRequestWrapper {

        public SessionAccessAwareRequest(HttpServletRequest request) {
            super(request);
        }

        @Override
        public HttpSession getSession() {
            return getSession(true);
        }

        @Override
        public HttpSession getSession(boolean create) {
            HttpSession session = super.getSession(create);
            if (session != null) {
                session.setAttribute(LAST_ACCESS_SESSION_ATTR, System.currentTimeMillis());
            }
            return session;
        }

    }

}

答案 1 :(得分:0)

不。 在Spring和您的应用程序有机会处理来自当前用户浏览器的HTTP请求之前,tomcat已根据Servlet规范更新了当前用户的会话。因此,Spring和您的应用程序无法控制续订当前用户的会话。

servlet-3_1-final.pdf的“ 7.6最后访问时间”显示为“ 当会话的一部分的请求首先由Servlet容器处理时,就认为该会话已被访问。”

如果Tomcat是Servlet容器。

调用Request.getSession()getSession(boolean create)时: 看到 Line 2955 of Request.javaLine 269 of Session.java

据说

“/**
* Update the accessed time information for this session.  This method
* should be called by the context when a request comes in for a particular
* session, even if the application does not reference it.
*/“  

相关的已实现类Line 675 of StandardSession.java 调用Request.getSession()getSession(boolean create)后,现有会话将通过更新thisAccessedTime

来更新

共享了一个调试过程,以查看现有会话的更新位置

  • Tomcat 9.0.38(未部署在集群中)
  • Spring框架版本4.3.12.RELEASE
  • 春季安全性版本4.2.4.RELEASE
  • Servlet 3.1

操作:

  1. 登录该应用程序,因此当前用户的会话已经存在。
  2. Line 686 of StandardSession.java上启用断点
  3. 单击任何UI按钮以触发HTTP请求。

请参见Line 511 of org.apache.catalina.authenticator.AuthenticatorBase 请参阅https://imgur.com/a/QJ3IJxh,它将逻辑显示为

  • 通过请求标头'Cookie中的会话ID查找会话: JSESSIONID'
  • 通过计算timeNow - thisAccessedTime
  • 检查会话是否仍然有效
  • 如果当前用户会话仍然有效,则使用访问时间更新thisAccessedTime。 这是更新会话的第一个地方,也是唯一的地方

在此方法上的以下调用不会调用access(),这可以在以下时间看到

Line 541 of org.apache.catalina.authenticator.AuthenticatorBase过滤器链处理中继续HTTP请求处理进度 Line 110 of org.springframework.security.web.context.HttpSessionSecurityContextRepository.javaLine 130 of org.springframework.security.web.session.ConcurrentSessionFilter.java 参见https://imgur.com/a/MfjJ2ey

只有Tomcat可以控制更新thisAccessedTime以续订现有会话的有效时间。

分辨率

实际上,目标是使效果看起来像某些特殊的HTTP请求(某些特殊的API,URL)无法续订在线用户会话,结果,如果只剩下这些API调用,就没有更多了其他正常的API调用会在有效的会话间隔内出现,然后使会话无效。

在选择以下选项之一之前,需要根据您的方案进行权衡

A>找到一个切入点,可以选择

  1. javax.servlet.Fitler的自定义实现 Servlet上下文过滤器链的末端
  2. 的定制实现 org.springframework.web.filter.GenericFilterBean位于末尾 Sprint内部过滤器链的过滤器链。
  3. 的定制实现 org.springframework.web.servlet.HandlerInterceptor

然后

  • 用于常规API调用:记录自定义的会话属性 thisAccessedTime
  • 用于特殊的API调用:使用会话属性thisAccessedTime检查会话是否仍然有效。如果已过期,则使会话无效

B>在Servlet上下文的过滤器链的末尾,javax.servlet.Fitler的定制过滤器实现 用完全自定义的会话生存期和会话事件实现包装Request.getSession()getSession(boolean create)

C> Websocket和Long Polling进行实时通信