我们最近意识到我们的会话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,并且正在寻找其他人可能提出的任何建议。谢谢!
答案 0 :(得分:1)
以下方法通过将会话cookie重命名为新的来解决问题,如果需要,将旧值复制到新的cookie名称。
当新cookie尚不存在时,它使用一些Apache模块(mod_headers和mod_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,则此方法有效。
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);