tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Pid...@pidster.com>
Subject Re: Share session cookie across subdomains
Date Sat, 19 Apr 2008 22:14:54 GMT
how about adding either, a property that allows you to specify the 
number of domain parts (accounting for domain.co.uk for example), or the 
actual domain?


p





Zach Cox wrote:
> 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
> {@link 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>
>  * &lt;Engine name="Catalina"...&gt;
>  *     &lt;Valve className="org.three3s.valves.CrossSubdomainSessionValve"/&gt;
>  * </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: users-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: users-help@tomcat.apache.org
> 
> 


---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Mime
View raw message