如何在生产中安全地更改会话cookie DOMAIN或名称?

时间:2015-09-24 13:55:42

标签: java session-cookies shiro

我们最近意识到我们的会话Cookie正在写入我们网站的完全限定域名www.myapp.com,例如:

MYAPPCOOKIE: 79D5DB83..., domain: www.myapp.com

我们希望将其切换为可以跨子域共享的cookie,因此myapp.com的任何服务器也可以使用会话cookie。例如,我们希望我们的cookie存储如下:

MYAPPCOOKIE: 79D5DB83..., domain: .myapp.com

我们尝试更改会话cookie以使用该域,如下所示:

   Cookie sessionCookie = sessionManager.getSessionIdCookie();
   sessionCookie.setDomain(".myapp.com");                

这在大多数案例中都可以正常使用。

我们发现在某些情况下部署此新代码后,某些用户无法登录。用户出现问题:

  • 已在当前浏览器会话中登录我们的网站,但目前尚未登录。
  • 他们尝试再次登录

它们的浏览器中似乎有2个会话cookie:

  • 来自其上一个会话的陈旧Cookie,其中包含完全限定的域名

    MYAPPCOOKIE: 79D5DB83..., domain: www.myapp.com
    
  • 他们刚刚登录的会话的新会话Cookie,使用新的域名设置

    MYAPPCOOKIE: 79D5DB83..., domain: .myapp.com
    

管理这个旧cookie的最佳方法是什么?如果用户没有会话,我们已经尝试添加一些代码来删除旧cookie,但是我们的应用程序中有一些路径似乎不起作用。

如果可能有效,我们愿意重命名Cookie,并且正在寻找其他人可能提出的任何建议。谢谢!

2 个答案:

答案 0 :(得分:1)

以下方法通过将会话cookie重命名为新的来解决问题,如果需要,将旧值复制到新的cookie名称。

当新cookie尚不存在时,它使用一些Apache模块(mod_headersmod_setenvif)将旧cookie值复制到新的cookie名称。

  # We only want to copy the old cookie to the new cookie name
  # when it doesnt already exist in the request. To do so, we
  # set a "per request" flag which is used to determine if we need
  # to do the "cookie copy operation".

  <IfModule mod_setenvif.c>
    SetEnvIf Cookie "NEW_SESSION_ID=" HAS_NEW_SESSION_ID
  </IfModule mod_setenvif.c>

  # If the cookie "NEW_SESSION_ID" doesnt exist in the request, copy 
  # the old cookie value to the new cookie name. This allows seamless 
  # switching to the new cookie for users with existing sessions, and
  # also ensure that if we have to rollback our code change for some 
  # reason, the old cookie will still be ok. Of course if new sessions 
  # are created with the new cookie, then they wouldn't be copied over on
  # rollback, but a similar approach could be used if someone 
  # wanted to do so

  <IfModule mod_headers.c>
     RequestHeader edit Cookie "OLD_SESSION_ID=([0-9a-zA-Z\-]*)" "NEW_SESSION_ID=$1 DEBUG_MSG_FOR_REPLACING=TRUE; OLD_SESSION_ID=$1" env=!HAS_NEW_SESSION_ID
  </IfModule mod_headers.c>

请注意,您可以通过查找DEBUG_MSG_FOR_REPLACING cookie值来测试替换是否有效,我已在替换完成时添加了该值以进行调试。

以下是端点的一些示例代码,它只是将cookie头的值转储到响应中,您可以在调试Apache更改时使用该代码:

@GET
@Path("dump_cookies")
public Object dumpCookies(@Context HttpServletRequest request)
{
  String result = request.getHeader("Cookie");
  String cookies = result.replace("; ", "<br/>");

  String html = "<h1>Raw</h1>" + result;

  html += "<hr/>";
  html += "<h1>Cookies</h1>" + cookies;

  return html;
}

请注意,由于业务原因导致我们无法重命名Cookie,因此我们最终未使用此解决方案。我们最终使用this solution instead,这使我们可以保持我们的Cookie名称相同。

答案 1 :(得分:0)

如果您正在使用Apache Shiro,并且在创建新cookie之前删除具有完全限定域名的旧cookie,则此方法有效。

ShiroSessionManager.java

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SessionContext;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;

public class ShiroSessionManager extends DefaultWebSessionManager
{
    @Override
    protected void onStart(Session session, SessionContext context)
    {
        if (isSessionIdCookieEnabled())
        {
            HttpServletRequest request = WebUtils.getHttpRequest(context);
            HttpServletResponse response = WebUtils.getHttpResponse(context);

            removeSessionCookieForFullDomain(request, response);
        }

        super.onStart(session, context);
    }

    private void removeSessionCookieForFullDomain(HttpServletRequest request, HttpServletResponse response)
    {
        Cookie sessionCookie = getSessionIdCookie();
        Cookie cookie = new SimpleCookie(sessionCookie.getName());

        cookie.setSecure(true);
        cookie.setHttpOnly(true);
        cookie.setComment(sessionCookie.getComment());

        // Setting the domain to null means use the fully qualified domain name
        cookie.setDomain(null);

        cookie.setMaxAge(sessionCookie.getMaxAge());
        cookie.setPath(sessionCookie.getPath());
        cookie.setValue(sessionCookie.getValue());
        cookie.setVersion(sessionCookie.getVersion());

        cookie.removeFrom(request, response);
    }
}

要使用此功能,您需要在安全管理器上设置会话管理器,例如:

    // Create our shiro environment
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    DefaultWebEnvironment environment = new DefaultWebEnvironment();
    DefaultWebSessionManager sessionManager = new ShiroSessionManager();

    // Use the new session manager
    securityManager.setSessionManager(sessionManager);
    environment.setWebSecurityManager(securityManager);
    SecurityUtils.setSecurityManager(securityManager);