I'm working on a site that has a single web app in Tomcat that handles lots of subdomains via wildcard DNS: - site.com - www.site.com - sub1.site.com - sub2.site.com - etc.
Basically, the site content gets customized based on the subdomain in the request, if any. However I ran into a problem where Tomcat would use separate JSESSIONID session cookies on each different domain which causes problems as the user would have multiple sessions if they switched subdomains. I found an examle Valve at http://www.esus.be/blog/?p=3 but it ultimately did not result in a cross-subdomain session cookie (at least in the Tomcat 6.0.14 I'm using). I'm posting the Valve that did actually seem to solve the problem for me below in hopes that it will help someone else with the same problem in the future. Basically you end up with a single session cookie with its domain set to .site.com so the client will send it back in the request for any subdomain. Usage: - compile CrossSubdomainSessionValve & put it in a .jar file - put that .jar file in $CATALINA_HOME/lib directory - include a <Valve className="org.three3s.valves.CrossSubdomainSessionValve"/> in $CATALINA_HOME/conf/server.xml If you see/experience any problems please let me know! Thanks, Zach package org.three3s.valves; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import org.apache.catalina.*; import org.apache.catalina.connector.*; import org.apache.catalina.valves.*; import org.apache.tomcat.util.buf.*; import org.apache.tomcat.util.http.*; /** <p>Replaces the domain of the session cookie generated by Tomcat with a domain that allows that * session cookie to be shared across subdomains. This valve digs down into the response headers * and replaces the Set-Cookie header for the session cookie, instead of futilely trying to * modify an existing Cookie object like the example at http://www.esus.be/blog/?p=3. That * approach does not work (at least as of Tomcat 6.0.14) because the * <code>org.apache.catalina.connector.Response.addCookieInternal</code> method renders the * cookie into the Set-Cookie response header immediately, making any subsequent modifying calls * on the Cookie object ultimately pointless.</p> * * <p>This results in a single, cross-subdomain session cookie on the client that allows the * session to be shared across all subdomains. However, see the [EMAIL PROTECTED] getCookieDomain(Request)} * method for limits on the subdomains.</p> * * <p>Note though, that this approach will fail if the response has already been committed. Thus, * this valve forces Tomcat to generate the session cookie and then replaces it before invoking * the next valve in the chain. Hopefully this is early enough in the valve-processing chain * that the response will not have already been committed. You are advised to define this * valve as early as possible in server.xml to ensure that the response has not already been * committed when this valve is invoked.</p> * * <p>We recommend that you define this valve in server.xml immediately after the Catalina Engine * as follows: * <pre> * <Engine name="Catalina"...> * <Valve className="org.three3s.valves.CrossSubdomainSessionValve"/> * </pre> * </p> */ public class CrossSubdomainSessionValve extends ValveBase { public CrossSubdomainSessionValve() { super(); info = "org.three3s.valves.CrossSubdomainSessionValve/1.0"; } @Override public void invoke(Request request, Response response) throws IOException, ServletException { //this will cause Request.doGetSession to create the session cookie if necessary request.getSession(true); //replace any Tomcat-generated session cookies with our own Cookie[] cookies = response.getCookies(); if (cookies != null) { for (int i = 0; i < cookies.length; i++) { Cookie cookie = cookies[i]; containerLog.debug("CrossSubdomainSessionValve: Cookie name is " + cookie.getName()); if (Globals.SESSION_COOKIE_NAME.equals(cookie.getName())) replaceCookie(request, response, cookie); } } //process the next valve getNext().invoke(request, response); } /** Replaces the value of the response header used to set the specified cookie to a value * with the cookie's domain set to the value returned by <code>getCookieDomain(request)</code> * * @param request * @param response * @param cookie cookie to be replaced. */ @SuppressWarnings("unchecked") protected void replaceCookie(Request request, Response response, Cookie cookie) { //copy the existing session cookie, but use a different domain Cookie newCookie = new Cookie(cookie.getName(), cookie.getValue()); if (cookie.getPath() != null) newCookie.setPath(cookie.getPath()); newCookie.setDomain(getCookieDomain(request)); newCookie.setMaxAge(cookie.getMaxAge()); newCookie.setVersion(cookie.getVersion()); if (cookie.getComment() != null) newCookie.setComment(cookie.getComment()); newCookie.setSecure(cookie.getSecure()); //if the response has already been committed, our replacement strategy will have no effect if (response.isCommitted()) containerLog.error("CrossSubdomainSessionValve: response was already committed!"); //find the Set-Cookie header for the existing cookie and replace its value with new cookie MimeHeaders headers = response.getCoyoteResponse().getMimeHeaders(); for (int i = 0, size = headers.size(); i < size; i++) { if (headers.getName(i).equals("Set-Cookie")) { MessageBytes value = headers.getValue(i); if (value.indexOf(cookie.getName()) >= 0) { StringBuffer buffer = new StringBuffer(); ServerCookie.appendCookieValue(buffer, newCookie.getVersion(), newCookie .getName(), newCookie.getValue(), newCookie.getPath(), newCookie .getDomain(), newCookie.getComment(), newCookie.getMaxAge(), newCookie .getSecure()); containerLog.debug("CrossSubdomainSessionValve: old Set-Cookie value: " + value.toString()); containerLog.debug("CrossSubdomainSessionValve: new Set-Cookie value: " + buffer); value.setString(buffer.toString()); } } } } /** Returns the last two parts of the specified request's server name preceded by a dot. * Using this as the session cookie's domain allows the session to be shared across subdomains. * Note that this implies the session can only be used with domains consisting of two or * three parts, according to the domain-matching rules specified in RFC 2109 and RFC 2965. * * <p>Examples:</p> * <ul> * <li>foo.com => .foo.com</li> * <li>www.foo.com => .foo.com</li> * <li>bar.foo.com => .foo.com</li> * <li>abc.bar.foo.com => .foo.com - this means cookie won't work on abc.bar.foo.com!</li> * </ul> * * @param request provides the server name used to create cookie domain. * @return the last two parts of the specified request's server name preceded by a dot. */ protected String getCookieDomain(Request request) { String cookieDomain = request.getServerName(); String[] parts = cookieDomain.split("\\."); if (parts.length >= 2) cookieDomain = parts[parts.length - 2] + "." + parts[parts.length - 1]; return "." + cookieDomain; } public String toString() { return ("CrossSubdomainSessionValve[container=" + container.getName() + ']'); } } --------------------------------------------------------------------- To start a new topic, e-mail: users@tomcat.apache.org To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]