session失效问题

场景描述

一个单体应用,启动两个服务,在同一个浏览器访问,登录上一个A服务后,再登录B服务,会导致A的session失效

分析

两个相同服务,彼此应该是没有影响的,但产生了影响,且不同浏览器就没有影响

源码

在一个登录过滤器中,有一段这样的代码,最终是由于session中没有userid导致表象的session失效,但实际上这个session已经不是之前携带userid的session了

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpSession session =  httpreq.getSession();
    String userid  = session.getAttribute("userid")==null?"":(String)session.getAttribute("userid");
    if(StringUtils.isEmpty(userid)){
        if(this.isAjaxRequest(httpreq)){
            httpres.getWriter().print("timeout");
            return;
        }
    }
}
 

关键在于以下代码

HttpSession session =  httpreq.getSession();

查看源码实现

RequestFacade:

@Override
public HttpSession getSession() {
​
    if (request == null) {
        throw new IllegalStateException(
            sm.getString("requestFacade.nullRequest"));
    }
​
    return getSession(true);
}
​
@Override
public HttpSession getSession(boolean create) {
​
    if (request == null) {
        throw new IllegalStateException(
            sm.getString("requestFacade.nullRequest"));
    }
​
    if (SecurityUtil.isPackageProtectionEnabled()){
        return AccessController.
            doPrivileged(new GetSessionPrivilegedAction(create));
    } else {
        return request.getSession(create);
    }
}

Request

@Override
public HttpSession getSession(boolean create) {
    Session session = doGetSession(create);
    if (session == null) {
        return null;
    }
​
    return session.getSession();
}
​
protected Session doGetSession(boolean create) {
​
    // There cannot be a session if no context has been assigned yet
    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 = null;
    if (context != null) {
        manager = context.getManager();
    }
    if (manager == null)
    {
        return (null);      // Sessions are not supported
    }
    if (requestedSessionId != null) {
        try {
            session = manager.findSession(requestedSessionId);
        } catch (IOException e) {
            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);
    }
    if ((context != null) && (response != null) &&
        context.getServletContext().getEffectiveSessionTrackingModes().
        contains(SessionTrackingMode.COOKIE) &&
        response.getResponse().isCommitted()) {
        throw new IllegalStateException
            (sm.getString("coyoteRequest.sessionCreateCommitted"));
    }
​
    // Attempt to reuse session id if one was submitted in a cookie
    // Do not reuse the session id if it is from a URL, to prevent possible
    // phishing attacks
    // Use the SSL session ID if one is present.
    if (("/".equals(context.getSessionCookiePath())
         && isRequestedSessionIdFromCookie()) || requestedSessionSSL ) {
        session = manager.createSession(getRequestedSessionId());
    } else {
        session = manager.createSession(null);
    }
​
    // Creating a new session cookie based on that session
    if ((session != null) && (getContext() != null)
        && getContext().getServletContext().
        getEffectiveSessionTrackingModes().contains(
            SessionTrackingMode.COOKIE)) {
        Cookie cookie =
            ApplicationSessionCookieConfig.createSessionCookie(
            context, session.getIdInternal(), isSecure());
​
        response.addSessionCookieInternal(cookie);
    }
​
    if (session == null) {
        return null;
    }
​
    session.access();
    return session;
}

以上有两段关键代码

// 1
if ((session != null) && !session.isValid()) {
    session = null;
}
// 2
// Creating a new session cookie based on that session
if ((session != null) && (getContext() != null)
    && getContext().getServletContext().
    getEffectiveSessionTrackingModes().contains(
        SessionTrackingMode.COOKIE)) {
    Cookie cookie =
        ApplicationSessionCookieConfig.createSessionCookie(
        context, session.getIdInternal(), isSecure());
​
    response.addSessionCookieInternal(cookie);
}

ApplicationSessionCookieConfig:

public static Cookie createSessionCookie(Context context,
            String sessionId, boolean secure) {
​
        SessionCookieConfig scc =
            context.getServletContext().getSessionCookieConfig();
​
        // NOTE: The priority order for session cookie configuration is:
        //       1. Context level configuration
        //       2. Values from SessionCookieConfig
        //       3. Defaults
        // 主要看这段,创建cookie,并将sessionId存储到cookie中
        Cookie cookie = new Cookie(
                SessionConfig.getSessionCookieName(context), sessionId);
  

SessionConfig:

/**
  * Determine the name to use for the session cookie for the provided
  * context.
  * @param context
 */
public static String getSessionCookieName(Context context) {
​
    String result = getConfiguredSessionCookieName(context);
​
    if (result == null) {
        result = DEFAULT_SESSION_COOKIE_NAME;
    }
​
    return result;
}
​
private static String getConfiguredSessionCookieName(Context context) {
​
    // Priority is:
    // 1. Cookie name defined in context
    // 2. Cookie name configured for app
    // 3. Default defined by spec
    if (context != null) {
        String cookieName = context.getSessionCookieName();
        if (cookieName != null && cookieName.length() > 0) {
            return cookieName;
        }
​
        SessionCookieConfig scc =
            context.getServletContext().getSessionCookieConfig();
        cookieName = scc.getName();
        if (cookieName != null && cookieName.length() > 0) {
            return cookieName;
        }
    }
​
    return null;
}

1: 验证session不为空且有效,根据结果(返回新的session)可知,之前的session已经失效,原因继续看

2: 1和2之前生成了新的session,此时的session是新产生的,将此sessionId存储到cookie中,也就是说获取session时应该会去cookie中找sessionId。

那么问题就很清晰了,相同的服务,开启两个,后一个(B服务)产生的session将sessionId存储至cookie中,导致A服务(前一个)的sessionId被覆盖(详见cookie),导致session失效,即1中session失效的原因,于是创建新的session,新的session中没有userid,导致A服务的表象的session失效(这里的表象是此应用的相关业务)。

cookie

根据SessionConfig的代码调试可知,本应用从context中获得到了cookieName为null,最终使用的是默认name:JSESSIONID,Cookie是面向路径的, 在一个目录页面里设置的 Cookie 在另一个目录的页面里是看不到的 ,但这两个服务都是在本地启动,域名都为localhost,所以会相互覆盖

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

future_1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值