Support for setting SessionTrackingMode
authormarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Mon, 12 Jan 2009 13:39:35 +0000 (13:39 +0000)
committermarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Mon, 12 Jan 2009 13:39:35 +0000 (13:39 +0000)
Most of this commit is to support invalidating the SSL session by the session manager.
I'm not sure yet that support for SSL session tracking is a good idea. There are quite a few limitations (see the ssl howto doc) and the changes to do it are fairly invasive. The option remains to remove this at a later date and not support SSL session tracking in Tomcat 7.

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@733748 13f79535-47bb-0310-9956-ffa450edef68

19 files changed:
java/org/apache/catalina/Globals.java
java/org/apache/catalina/connector/CoyoteAdapter.java
java/org/apache/catalina/connector/Request.java
java/org/apache/catalina/connector/Response.java
java/org/apache/catalina/core/ApplicationContext.java
java/org/apache/catalina/core/ApplicationContextFacade.java
java/org/apache/catalina/core/LocalStrings.properties
java/org/apache/catalina/ha/session/JvmRouteBinderValve.java
java/org/apache/catalina/session/Constants.java
java/org/apache/catalina/session/ManagerBase.java
java/org/apache/coyote/ActionCode.java
java/org/apache/coyote/http11/Http11AprProcessor.java
java/org/apache/coyote/http11/Http11NioProcessor.java
java/org/apache/coyote/http11/Http11Processor.java
java/org/apache/tomcat/util/net/AprEndpoint.java
java/org/apache/tomcat/util/net/SSLSessionManager.java [new file with mode: 0644]
java/org/apache/tomcat/util/net/SSLSupport.java
java/org/apache/tomcat/util/net/jsse/JSSESupport.java
webapps/docs/ssl-howto.xml

index 0d0125b..dcfeaf4 100644 (file)
@@ -145,6 +145,14 @@ public final class Globals {
 
 
     /**
+     * The request attribute key for the session manager.
+     * This one is a Tomcat extension to the Servlet spec.
+     */
+    public static final String SSL_SESSION_MGR_ATTR =
+        "javax.servlet.request.ssl_session_mgr";
+
+
+    /**
      * The servlet context attribute under which the managed bean Registry
      * will be stored for privileged contexts (if enabled).
      */
index 977b6d2..731b540 100644 (file)
@@ -19,6 +19,9 @@
 package org.apache.catalina.connector;
 
 import java.io.IOException;
+import java.util.EnumSet;
+
+import javax.servlet.SessionTrackingMode;
 
 import org.apache.catalina.CometEvent;
 import org.apache.catalina.Context;
@@ -36,6 +39,7 @@ import org.apache.tomcat.util.buf.CharChunk;
 import org.apache.tomcat.util.buf.MessageBytes;
 import org.apache.tomcat.util.http.Cookies;
 import org.apache.tomcat.util.http.ServerCookie;
+import org.apache.tomcat.util.net.SSLSupport;
 import org.apache.tomcat.util.net.SocketStatus;
 
 
@@ -55,6 +59,8 @@ public class CoyoteAdapter
 
     // -------------------------------------------------------------- Constants
 
+    private static final EnumSet<SessionTrackingMode> SSL_ONLY =
+        EnumSet.of(SessionTrackingMode.SSL);
 
     public static final int ADAPTER_NOTES = 1;
 
@@ -505,12 +511,32 @@ public class CoyoteAdapter
 
         // Parse session Id
         parseSessionCookiesId(req, request);
-
+        parseSessionSslId(request);
         return true;
     }
 
 
     /**
+     * Look for SSL sesison ID if required. Only look for SSL Session ID if it
+     * is the only tracking method enabled.
+     */
+    protected void parseSessionSslId(Request request) {
+        if (request.getRequestedSessionId() == null &&
+                SSL_ONLY.equals(request.getServletContext()
+                        .getEffectiveSessionTrackingModes()) &&
+                Boolean.TRUE.equals(
+                        request.getConnector().getAttribute("SSLEnabled"))) {
+            // TODO Is there a better way to map SSL sessions to our sesison ID?
+            // TODO The request.getAttribute() will cause a number of other SSL
+            //      attribute to be populated. Is this a performance concern?
+            request.setRequestedSessionId(
+                    request.getAttribute(SSLSupport.SESSION_ID_KEY).toString());
+            request.setRequestedSessionSSL(true);
+        }
+    }
+    
+    
+    /**
      * Parse session id in URL.
      */
     protected void parseSessionId(org.apache.coyote.Request req, Request request) {
@@ -518,7 +544,9 @@ public class CoyoteAdapter
         ByteChunk uriBC = req.requestURI().getByteChunk();
         int semicolon = uriBC.indexOf(match, 0, match.length(), 0);
 
-        if (semicolon > 0) {
+        if (semicolon > 0 &&
+                request.getServletContext().getEffectiveSessionTrackingModes()
+                        .contains(SessionTrackingMode.URL)) {
 
             // Parse session ID, and extract it from the decoded request URI
             int start = uriBC.getStart();
@@ -563,7 +591,9 @@ public class CoyoteAdapter
         // from a parent context with a session ID may be present which would
         // overwrite the valid session ID encoded in the URL
         Context context = (Context) request.getMappingData().context;
-        if (context != null && !context.getCookies())
+        if (context != null && !context.getServletContext()
+                .getEffectiveSessionTrackingModes().contains(
+                        SessionTrackingMode.COOKIE))
             return;
         
         // Parse session id from cookies
index b92bb07..6f5699a 100644 (file)
@@ -46,6 +46,7 @@ import javax.servlet.ServletRequestAttributeEvent;
 import javax.servlet.ServletRequestAttributeListener;
 import javax.servlet.ServletResponse;
 import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
@@ -333,6 +334,12 @@ public class Request
 
 
     /**
+     * Was the requested session ID obtained from the SSL session?
+     */
+    protected boolean requestedSessionSSL = false;
+
+
+    /**
      * Parse locales.
      */
     protected boolean localesParsed = false;
@@ -1505,8 +1512,7 @@ public class Request
     }
 
     public ServletContext getServletContext() {
-        // TODO SERVLET3
-        return null;
+       return context.getServletContext();
     }
 
     public boolean isAsyncStarted() {
@@ -1736,6 +1742,20 @@ public class Request
 
 
     /**
+     * Set a flag indicating whether or not the requested session ID for this
+     * request came in through SSL.  This is normally called by the
+     * HTTP Connector, when it parses the request headers.
+     *
+     * @param flag The new flag
+     */
+    public void setRequestedSessionSSL(boolean flag) {
+
+        this.requestedSessionSSL = flag;
+
+    }
+
+
+    /**
      * Set the unparsed request URI for this Request.  This will normally be
      * called by the HTTP Connector, when it parses the request headers.
      *
@@ -2321,6 +2341,15 @@ public class Request
         coyoteRequest.action(ActionCode.ACTION_COMET_SETTIMEOUT,new Long(timeout));
     }
     
+    /**
+     * Not part of Servlet 3 spec but probably should be.
+     * @return
+     */
+    public boolean isRequestedSessionIdFromSSL() {
+        return requestedSessionSSL;
+    }
+    
+    
     // ------------------------------------------------------ Protected Methods
 
 
@@ -2360,7 +2389,8 @@ public class Request
         if (!create)
             return (null);
         if ((context != null) && (response != null) &&
-            context.getCookies() &&
+            context.getServletContext().getEffectiveSessionTrackingModes().
+                    contains(SessionTrackingMode.COOKIE) &&
             response.getResponse().isCommitted()) {
             throw new IllegalStateException
               (sm.getString("coyoteRequest.sessionCreateCommitted"));
@@ -2369,16 +2399,26 @@ public class Request
         // 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
-        if (connector.getEmptySessionPath() 
-                && isRequestedSessionIdFromCookie()) {
+        // Use the SSL session ID if one is present. 
+        if ((connector.getEmptySessionPath() 
+                && isRequestedSessionIdFromCookie()) || requestedSessionSSL ) {
             session = manager.createSession(getRequestedSessionId());
+            if (requestedSessionSSL) {
+                coyoteRequest.action(ActionCode.ACTION_REQ_SSL_SESSION_MGR,
+                        null);
+                session.setNote(
+                        org.apache.catalina.session.Constants.SESS_SSL_MGMT,
+                        getAttribute(Globals.SSL_SESSION_MGR_ATTR));
+            }
         } else {
             session = manager.createSession(null);
         }
 
         // Creating a new session cookie based on that session
         if ((session != null) && (getContext() != null)
-               && getContext().getCookies()) {
+               && getContext().getServletContext().
+                       getEffectiveSessionTrackingModes().contains(
+                               SessionTrackingMode.COOKIE)) {
             Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,
                                        session.getIdInternal());
             configureSessionCookie(cookie);
index 4b0e55c..349b49d 100644 (file)
@@ -35,6 +35,7 @@ import java.util.TimeZone;
 import java.util.Vector;
 
 import javax.servlet.ServletOutputStream;
+import javax.servlet.SessionTrackingMode;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletResponse;
 
@@ -1420,6 +1421,11 @@ public class Response
         if (hreq.isRequestedSessionIdFromCookie())
             return (false);
         
+        // Is URL encoding permitted
+        if (!hreq.getServletContext().getEffectiveSessionTrackingModes().
+                contains(SessionTrackingMode.URL))
+            return false;
+
         if (SecurityUtil.isPackageProtectionEnabled()) {
             return (
                 AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
index e2cd2e1..29bc2c8 100644 (file)
@@ -44,8 +44,10 @@ import javax.servlet.SessionCookieConfig;
 import javax.servlet.SessionTrackingMode;
 
 import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
 import org.apache.catalina.Host;
 import org.apache.catalina.Wrapper;
+import org.apache.catalina.connector.Connector;
 import org.apache.catalina.deploy.ApplicationParameter;
 import org.apache.catalina.util.Enumerator;
 import org.apache.catalina.util.ResourceSet;
@@ -85,6 +87,9 @@ public class ApplicationContext
         super();
         this.context = context;
         this.basePath = basePath;
+        
+        // Populate default session tracking modes
+        populateDefaultSessionTrackingModes();
     }
 
 
@@ -154,6 +159,12 @@ public class ApplicationContext
      * Session Cookie config
      */
     private SessionCookieConfig sessionCookieConfig;
+    
+    /**
+     * Session tracking modes
+     */
+    private EnumSet<SessionTrackingMode> sessionTrackingModes = null;
+    private EnumSet<SessionTrackingMode> defaultSessionTrackingModes = null;
 
     // --------------------------------------------------------- Public Methods
 
@@ -840,15 +851,50 @@ public class ApplicationContext
     }
 
 
+    /**
+     * By default {@link SessionTrackingMode#URL} is always supported, {@link
+     * SessionTrackingMode#COOKIE} is supported unless the <code>cookies</code>
+     * attribute has been set to <code>false</code> for the context and {@link
+     * SessionTrackingMode#SSL} is supported if at least one of the connectors
+     * used by this context has the attribute <code>SSLEnabled</code> set to
+     * <code>true</code>.
+     */
     public EnumSet<SessionTrackingMode> getDefaultSessionTrackingModes() {
-        // TODO SERVLET3
-        return null;
+        return defaultSessionTrackingModes;
     }
 
+    private void populateDefaultSessionTrackingModes() {
+        // URL re-writing is always enabled by default
+        defaultSessionTrackingModes = EnumSet.of(SessionTrackingMode.URL); 
+        
+        if (context.getCookies()) {
+            defaultSessionTrackingModes.add(SessionTrackingMode.COOKIE);
+        }
+
+        // Context > Host > Engine > Service
+        Connector[] connectors = ((Engine) context.getParent().getParent())
+                .getService().findConnectors();
+        // Need at least one SSL enabled connector to use the SSL session ID.
+        // has to be SSL enabled so we can close the SSL session.
+        // TODO extend this for SSL sessions managed by accelerators, web
+        // servers etc
+        for (Connector connector : connectors) {
+            if (Boolean.TRUE.equals(connector.getAttribute("SSLEnabled"))) {
+                defaultSessionTrackingModes.add(SessionTrackingMode.SSL);
+                break;
+            }
+        }
+    }
 
+    /**
+     * Return the supplied value if one was previously set, else return the
+     * defaults.
+     */
     public EnumSet<SessionTrackingMode> getEffectiveSessionTrackingModes() {
-        // TODO SERVLET3
-        return null;
+        if (sessionTrackingModes != null) {
+            return sessionTrackingModes;
+        }
+        return defaultSessionTrackingModes;
     }
 
 
@@ -862,9 +908,33 @@ public class ApplicationContext
     }
 
 
+    /**
+     * @throws IllegalStateException if the context has already been initialised
+     * @throws IllegalArgumentException TODO SERVLET3 Something to do with SSL
+     *                                  but the spec language is not clear
+     *                                  If an unsupported tracking mode is
+     *                                  requested
+     */
     public void setSessionTrackingModes(
             EnumSet<SessionTrackingMode> sessionTrackingModes) {
-        // TODO SERVLET3
+
+        if (context.getAvailable()) {
+            throw new IllegalStateException(
+                    sm.getString("applicationContext.setSessionTracking.ise",
+                            getContextPath()));
+        }
+        
+        // Check that only supported tracking modes have been requested
+        for (SessionTrackingMode sessionTrackingMode : sessionTrackingModes) {
+            if (!defaultSessionTrackingModes.contains(sessionTrackingMode)) {
+                throw new IllegalArgumentException(sm.getString(
+                        "applicationContext.setSessionTracking.iae",
+                        sessionTrackingMode.toString(), getContextPath()));
+            }
+        }
+        // TODO SERVLET3 - The SSL test
+        
+        this.sessionTrackingModes = sessionTrackingModes;
     }
 
 
index 8f5a3e9..b88792f 100644 (file)
@@ -105,6 +105,9 @@ public final class ApplicationContextFacade
         classCache.put("getRealPath", clazz);
         classCache.put("getAttribute", clazz);
         classCache.put("log", clazz);
+        classCache.put("getDefaultSessionTrackingModes", clazz);
+        classCache.put("getEffectiveSessionTrackingModes", clazz);
+        classCache.put("setSessionTrackingModes", clazz);
     }
 
 
@@ -396,14 +399,22 @@ public final class ApplicationContextFacade
 
 
     public EnumSet<SessionTrackingMode> getDefaultSessionTrackingModes() {
-        // TODO SERVLET3
-        return null;
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (EnumSet<SessionTrackingMode>)
+                doPrivileged("getDefaultSessionTrackingModes", null);
+        } else {
+            return context.getDefaultSessionTrackingModes();
+        }
     }
 
 
     public EnumSet<SessionTrackingMode> getEffectiveSessionTrackingModes() {
-        // TODO SERVLET3
-        return null;
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (EnumSet<SessionTrackingMode>)
+                doPrivileged("getEffectiveSessionTrackingModes", null);
+        } else {
+            return context.getEffectiveSessionTrackingModes();
+        }
     }
 
 
@@ -420,7 +431,12 @@ public final class ApplicationContextFacade
 
     public void setSessionTrackingModes(
             EnumSet<SessionTrackingMode> sessionTrackingModes) {
-        // TODO SERVLET3
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("setSessionTrackingModes",
+                    new Object[]{sessionTrackingModes});
+        } else {
+            context.setSessionTrackingModes(sessionTrackingModes);
+        }
     }
 
 
index 28f4eca..03efc67 100644 (file)
@@ -18,6 +18,8 @@ applicationContext.mapping.error=Error during mapping
 applicationContext.requestDispatcher.iae=Path {0} does not start with a "/" character
 applicationContext.resourcePaths.iae=Path {0} does not start with a "/" character
 applicationContext.setAttribute.namenull=Name cannot be null
+applicationContext.setSessionTracking.ise=The session tracking modes for context {0} cannot be set whilst the context is running
+applicationContext.setSessionTracking.iae=The session tracking mode {0} requested for context {1} is not supported by that context
 applicationDispatcher.allocateException=Allocate exception for servlet {0}
 applicationDispatcher.deallocateException=Deallocate exception for servlet {0}
 applicationDispatcher.forward.ise=Cannot forward after response has been committed
index 14644a2..1316728 100644 (file)
@@ -19,6 +19,7 @@ package org.apache.catalina.ha.session;
 import java.io.IOException;
 
 import javax.servlet.ServletException;
+import javax.servlet.SessionTrackingMode;
 import javax.servlet.http.Cookie;
 
 import org.apache.catalina.Container;
@@ -431,7 +432,8 @@ public class JvmRouteBinderValve extends ValveBase implements ClusterValve, Life
                                        Response response, String sessionId) {
         if (response != null) {
             Context context = request.getContext();
-            if (context.getCookies()) {
+            if (context.getServletContext().getEffectiveSessionTrackingModes()
+                    .contains(SessionTrackingMode.COOKIE)) {
                 // set a new session cookie
                 Cookie newCookie = new Cookie(Globals.SESSION_COOKIE_NAME,
                         sessionId);
index 2454687..ca4b09a 100644 (file)
@@ -29,4 +29,10 @@ public class Constants {
 
     public static final String Package = "org.apache.catalina.session";
 
+    /**
+     * Name of note containing SSL session manager
+     */
+    public static final String SESS_SSL_MGMT =
+        "org.apache.catalina.session.SSL_MGMT";
+
 }
index 8f5749f..5624c9a 100644 (file)
@@ -53,6 +53,7 @@ import org.apache.catalina.util.StringManager;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.modeler.Registry;
+import org.apache.tomcat.util.net.SSLSessionManager;
 
 
 /**
@@ -907,6 +908,12 @@ public abstract class ManagerBase implements Manager, MBeanRegistration {
     public void remove(Session session) {
 
         sessions.remove(session.getIdInternal());
+        // Close the underlying SSL session
+        SSLSessionManager mgr =
+            (SSLSessionManager) session.getNote(Constants.SESS_SSL_MGMT);
+        if (mgr != null) {
+            mgr.invalidateSession();
+        }
 
     }
 
index ac2e0fc..eb55f76 100644 (file)
@@ -17,6 +17,8 @@
  
 package org.apache.coyote;
 
+import org.apache.tomcat.util.net.SSLSessionManager;
+
 
 /**
  * Enumerated class containing the adapter event codes.
@@ -161,6 +163,12 @@ public final class ActionCode {
      */
     public static final ActionCode ACTION_COMET_SETTIMEOUT = new ActionCode(25);
     
+    /**
+     * Callback for lazy evaluation - obtain the SSL Session Manager
+     */
+    public static final ActionCode ACTION_REQ_SSL_SESSION_MGR =
+        new ActionCode(26);
+    
     // ----------------------------------------------------------- Constructors
     int code;
 
index fae37f9..78446df 100644 (file)
@@ -1198,6 +1198,9 @@ public class Http11AprProcessor implements ActionHook {
             //no op
         } else if (actionCode == ActionCode.ACTION_COMET_SETTIMEOUT) {
             //no op
+        } else if (actionCode == ActionCode.ACTION_REQ_SSL_SESSION_MGR) {
+            //TODO SERVLET3 provide a hook to enable the SSL session to be
+            // invalidated
         }
 
     }
index 118a7e6..e6768c7 100644 (file)
@@ -1236,8 +1236,11 @@ public class Http11NioProcessor implements ActionHook {
             RequestInfo rp = request.getRequestProcessor();
             if ( rp.getStage() != org.apache.coyote.Constants.STAGE_SERVICE ) //async handling
                 attach.setTimeout(timeout);
+        } else if (actionCode == ActionCode.ACTION_REQ_SSL_SESSION_MGR) {
+            if( sslSupport != null) {
+                request.setAttribute(SSLSupport.SESSION_MGR, sslSupport);
+            }
         }
-
     }
 
 
index 7a0a3dd..0530588 100644 (file)
@@ -1105,6 +1105,10 @@ public class Http11Processor implements ActionHook {
             InternalInputBuffer internalBuffer = (InternalInputBuffer)
                 request.getInputBuffer();
             internalBuffer.addActiveFilter(savedBody);
+        } else if (actionCode == ActionCode.ACTION_REQ_SSL_SESSION_MGR) {
+            if( sslSupport != null) {
+                request.setAttribute(SSLSupport.SESSION_MGR, sslSupport);
+            }
         }
 
     }
index 1127f07..84919c8 100644 (file)
@@ -87,6 +87,13 @@ public class AprEndpoint {
      */
     public static final String SESSION_ID_KEY = "javax.servlet.request.ssl_session";
 
+    /**
+     * The request attribute key for the session manager.
+     * This one is a Tomcat extension to the Servlet spec.
+     */
+    public static final String SESSION_MGR =
+        "javax.servlet.request.ssl_session_mgr";
+
 
     // ----------------------------------------------------------------- Fields
 
diff --git a/java/org/apache/tomcat/util/net/SSLSessionManager.java b/java/org/apache/tomcat/util/net/SSLSessionManager.java
new file mode 100644 (file)
index 0000000..0269311
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net;
+
+/**
+ * Defines an interface used to manage SSL sessions. The manager operates on a
+ * single session.
+ * 
+ * $Id$
+ */
+public interface SSLSessionManager {
+    /**
+     * Invalidate the specified SSL session
+     * @param   sessionId   The ID of the session to invalidate.
+     */
+    public void invalidateSession();
+}
index 9ffe605..f1437e0 100644 (file)
@@ -49,6 +49,14 @@ public interface SSLSupport {
     public static final String SESSION_ID_KEY = "javax.servlet.request.ssl_session";
 
     /**
+     * The request attribute key for the session manager.
+     * This one is a Tomcat extension to the Servlet spec.
+     */
+    public static final String SESSION_MGR =
+        "javax.servlet.request.ssl_session_mgr";
+
+    
+    /**
      * A mapping table to determine the number of effective bits in the key
      * when using a cipher suite containing the specified cipher name.  The
      * underlying data came from the TLS Specification (RFC 2246), Appendix C.
index 11510f2..fd8dde5 100644 (file)
@@ -31,6 +31,7 @@ import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocket;
 import javax.security.cert.X509Certificate;
 
+import org.apache.tomcat.util.net.SSLSessionManager;
 import org.apache.tomcat.util.net.SSLSupport;
 
 /** JSSESupport
@@ -48,7 +49,7 @@ import org.apache.tomcat.util.net.SSLSupport;
    Parts cribbed from CertificatesValve
 */
 
-class JSSESupport implements SSLSupport {
+class JSSESupport implements SSLSupport, SSLSessionManager {
     
     private static org.apache.juli.logging.Log log =
         org.apache.juli.logging.LogFactory.getLog(JSSESupport.class);
@@ -232,5 +233,11 @@ class JSSESupport implements SSLSupport {
         }
     }
 
+    /**
+     * Invalidate the session this support object is associated with.
+     */
+    public void invalidateSession() {
+        session.invalidate();
+    }
 }
 
index 8bc0b33..d4dcabe 100644 (file)
@@ -284,18 +284,21 @@ which contains further references for this issue.</p>
 <subsection name="Edit the Tomcat Configuration File">
 <p>If you are using APR, you have the option of configuring an alternative engine to OpenSSL.
 <source>
-&lt;Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="someengine" SSLRandomSeed="somedevice" /&gt;
+&lt;Listener className="org.apache.catalina.core.AprLifecycleListener"
+          SSLEngine="someengine" SSLRandomSeed="somedevice" /&gt;
 </source>
 The default value is
 <source>
-&lt;Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" SSLRandomSeed="builtin" /&gt;
+&lt;Listener className="org.apache.catalina.core.AprLifecycleListener"
+          SSLEngine="on" SSLRandomSeed="builtin" /&gt;
 </source>
 So to use SSL under APR, make sure the SSLEngine attribute is set to something other than <code>off</code>.
 The default value is <code>on</code> and if you specify another value, it has to be a valid engine name.
 <br/>
 If you haven't compiled in SSL support into your Tomcat Native library, then you can turn this initialization off
 <source>
-&lt;Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="off" /&gt;
+&lt;Listener className="org.apache.catalina.core.AprLifecycleListener"
+          SSLEngine="off" /&gt;
 </source>
 SSLRandomSeed allows to specify a source of entropy. Productive system needs a reliable source of entropy
 but entropy may need a lot of time to be collected therefore test systems could use no blocking entropy
@@ -634,6 +637,67 @@ information, at
 
 </section>
 
+<section name="Using the SSL for session tracking in your application">
+  <p>This is a new feature in the Servlet 3.0 specification. Because is uses the
+     SSL session ID associated with the physical client server connection there
+     are a number of limitations. They are:
+    <ul>
+      <li>The SSL connection must be managed by Tomcat, i.e. Tomcat must have a
+          connector with the attribute <strong>SSLEnabled</strong> set to
+          <code>true</code>. This is to enable Tomcat to invalidate the SSL
+          session if the HTTP session is invalidated. If SSL conections are
+          managed by a proxy or a hardware accelerator this is not possibe.</li>
+      <li>It cannot be used in conjunction with session replication as the SSL
+          session IDs will be different on each node.</li>
+      <li>When <code>session.invalidate()</code> is called within the application
+          <code>response.setHeader("Connection", "close")</code> must also be
+          called as invalidating the session does not affect any current
+          connections.</li>
+      <li>HTTP session timeouts, keep-alive timeouts and SSL session timeouts
+          should be consistent. Note that the default JSSE SSL session timeout
+          (24 hours) is significantly longer than the default Tomcat HTTP Sesson
+          timeout (30 minutes).</li>
+    </ul>
+  </p>
+
+  <p>
+    To enable SSL session tracking you need to use a context listener to set the
+    tracking mode for the context to be just SSL (if any other tracking mode is
+    enabled, it will be used in preference). It might look something like:
+    <source>
+package org.apache.tomcat.example;
+
+import java.util.EnumSet;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.SessionTrackingMode;
+
+public class SessionTrackingModeListener implements ServletContextListener {
+
+    @Override
+    public void contextDestroyed(ServletContextEvent event) {
+        // Do nothing
+    }
+
+    @Override
+    public void contextInitialized(ServletContextEvent event) {
+        ServletContext context = event.getServletContext();
+        EnumSet&lt;SessionTrackingMode&gt; modes =
+            EnumSet.of(SessionTrackingMode.SSL);
+        
+        context.setSessionTrackingModes(modes);
+    }
+
+}
+    </source>
+  </p>
+  <p>Note: SSL session tracking is implemented for the BIO and NIO connetcors.
+     It is not yet implemented for the APR connector.</p>
+     
+</section>
+
 <section name="Miscellaneous Tips and Bits">
 
 <p>To access the SSL session ID from the request, use:<br />