破案!!!分布式环境下session失效现象

95 阅读2分钟

在getSession会调用doGetSession org.apache.catalina.connector.Request#doGetSession

// org.apache.catalina.connector.Request#getSession()
public HttpSession getSession() {
    Session session = doGetSession(true);
    if (session == null) {
        return null;
    }

    return session.getSession();
}

doGetSession核心的逻辑

  1. session是否存在存在的话是否有效
  2. 然后查看cookie里携带的sessionId在当前tomcat里是否有 + 是否有效
  3. 如果携带的sessionId无效,最后会生成一个新的sessionId(manager.createSession(sessionId))
protected Session doGetSession(boolean create) {

    // There cannot be a session if no context has been assigned yet
    Context context = getContext();
    if (context == null) {
        return null;
    }

    // Return the current session if it exists and is valid
    if ((session != null) && !session.isValid()) {
        session = null;
    }
    if (session != null) {
        return session;
    }

    // Return the requested session if it exists and is valid
    Manager manager = context.getManager();
    if (manager == null) {
        return null; // Sessions are not supported
    }
    if (requestedSessionId != null) {
        try {
            session = manager.findSession(requestedSessionId);
        } catch (IOException e) {
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("request.session.failed", requestedSessionId, e.getMessage()), e);
            } else {
                log.info(sm.getString("request.session.failed", requestedSessionId, e.getMessage()));
            }
            session = null;
        }
        if ((session != null) && !session.isValid()) {
            session = null;
        }
        if (session != null) {
            session.access();
            return session;
        }
    }

    // Create a new session if requested and the response is not committed
    if (!create) {
        return null;
    }
    boolean trackModesIncludesCookie =
            context.getServletContext().getEffectiveSessionTrackingModes().contains(SessionTrackingMode.COOKIE);
    if (trackModesIncludesCookie && response.getResponse().isCommitted()) {
        throw new IllegalStateException(sm.getString("coyoteRequest.sessionCreateCommitted"));
    }

    // Re-use session IDs provided by the client in very limited
    // circumstances.
    String sessionId = getRequestedSessionId();
    if (requestedSessionSSL) {
        // If the session ID has been obtained from the SSL handshake then
        // use it.
    } else if (("/".equals(context.getSessionCookiePath()) && isRequestedSessionIdFromCookie())) {
        /*
         * This is the common(ish) use case: using the same session ID with multiple web applications on the same
         * host. Typically this is used by Portlet implementations. It only works if sessions are tracked via
         * cookies. The cookie must have a path of "/" else it won't be provided for requests to all web
         * applications.
         *
         * Any session ID provided by the client should be for a session that already exists somewhere on the host.
         * Check if the context is configured for this to be confirmed.
         */
        if (context.getValidateClientProvidedNewSessionId()) {
            boolean found = false;
            for (Container container : getHost().findChildren()) {
                Manager m = ((Context) container).getManager();
                if (m != null) {
                    try {
                        if (m.findSession(sessionId) != null) {
                            found = true;
                            break;
                        }
                    } catch (IOException e) {
                        // Ignore. Problems with this manager will be
                        // handled elsewhere.
                    }
                }
            }
            if (!found) {
                sessionId = null;
            }
        }
    } else {
        sessionId = null;
    }
    session = manager.createSession(sessionId);

    // Creating a new session cookie based on that session
    if (session != null && trackModesIncludesCookie) {
        Cookie cookie =
                ApplicationSessionCookieConfig.createSessionCookie(context, session.getIdInternal(), isSecure());

        response.addSessionCookieInternal(cookie);
    }

    if (session == null) {
        return null;
    }

    session.access();
    return session;
}

总结

在分布式情况下。拿着其他服务器的sessionId请求新的服务器时会返回新的sessionID。然后再拿这个sessionId请求其他的服务器又会产生新的sessionId。如此反复,最终失效了。