You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2020/02/28 14:25:05 UTC

[tomcat] branch 7.0.x updated: Add an option to persist authentication information with the session

This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 7.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/7.0.x by this push:
     new 02e97d2  Add an option to persist authentication information with the session
02e97d2 is described below

commit 02e97d2b98f28ceeb5dd2d2c9636823a146d55f7
Author: Carsten Klein <c....@datagis.com>
AuthorDate: Wed Feb 19 09:01:04 2020 +0100

    Add an option to persist authentication information with the session
    
    Patch provided by Carsten Klein.
---
 .../catalina/session/LocalStrings.properties       |  2 +
 java/org/apache/catalina/session/ManagerBase.java  | 37 +++++++++++-
 .../apache/catalina/session/StandardSession.java   | 69 +++++++++++++++++++++-
 .../apache/catalina/session/mbeans-descriptors.xml |  8 +++
 webapps/docs/changelog.xml                         |  6 ++
 webapps/docs/config/manager.xml                    | 62 ++++++++++++++++++-
 6 files changed, 177 insertions(+), 7 deletions(-)

diff --git a/java/org/apache/catalina/session/LocalStrings.properties b/java/org/apache/catalina/session/LocalStrings.properties
index 2429627..f969a8f 100644
--- a/java/org/apache/catalina/session/LocalStrings.properties
+++ b/java/org/apache/catalina/session/LocalStrings.properties
@@ -93,6 +93,8 @@ standardSession.isNew.ise=isNew: Session already invalidated
 standardSession.logoutfail=Exception logging out user when expiring session
 standardSession.notDeserializable=Cannot deserialize session attribute [{0}] for session [{1}]
 standardSession.notSerializable=Cannot serialize session attribute [{0}] for session [{1}]
+standardSession.principalNotDeserializable=Cannot deserialize Principal object for session [{0}]
+standardSession.principalNotSerializable=Cannot serialize Principal object for session [{0}]
 standardSession.removeAttribute.ise=removeAttribute: Session already invalidated
 standardSession.sessionCreated=Created Session id = [{0}]
 standardSession.sessionEvent=Session event listener threw exception
diff --git a/java/org/apache/catalina/session/ManagerBase.java b/java/org/apache/catalina/session/ManagerBase.java
index 8022d08..af0add0 100644
--- a/java/org/apache/catalina/session/ManagerBase.java
+++ b/java/org/apache/catalina/session/ManagerBase.java
@@ -233,6 +233,12 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
 
     private boolean warnOnSessionAttributeFilterFailure;
 
+    /**
+     * Determines whether sessions managed by this manager shall persist (serialize)
+     * authentication information or not.
+     */
+    private boolean persistAuthentication = false;
+
 
     // ------------------------------------------------------------ Constructors
 
@@ -240,8 +246,11 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
         if (Globals.IS_SECURITY_ENABLED) {
             // Minimum set required for default distribution/persistence to work
             // plus String
+            // plus SerializablePrincipal and String[] (required for authentication persistence)
             setSessionAttributeValueClassNameFilter(
-                    "java\\.lang\\.(?:Boolean|Integer|Long|Number|String)");
+                    "java\\.lang\\.(?:Boolean|Integer|Long|Number|String)"
+                    + "|org\\.apache\\.catalina\\.realm\\.GenericPrincipal\\$SerializablePrincipal"
+                    + "|\\[Ljava.lang.String;");
             setWarnOnSessionAttributeFilterFailure(true);
         }
     }
@@ -638,10 +647,34 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
                                    Integer.valueOf(this.processExpiresFrequency));
 
     }
-    // --------------------------------------------------------- Public Methods
 
 
     /**
+     * Return whether sessions managed by this manager shall persist authentication
+     * information or not.
+     *
+     * @return {@code true}, sessions managed by this manager shall persist
+     *         authentication information; {@code false} otherwise
+     */
+    public boolean getPersistAuthentication() {
+        return this.persistAuthentication;
+    }
+
+    /**
+     * Set whether sessions managed by this manager shall persist authentication
+     * information or not.
+     *
+     * @param persistAuthentication if {@code true}, sessions managed by this manager
+     *                              shall persist authentication information
+     */
+    public void setPersistAuthentication(boolean persistAuthentication) {
+        this.persistAuthentication = persistAuthentication;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
      * {@inheritDoc}
      * <p>
      * Direct call to {@link #processExpires()}
diff --git a/java/org/apache/catalina/session/StandardSession.java b/java/org/apache/catalina/session/StandardSession.java
index b1a0693..0c87fea 100644
--- a/java/org/apache/catalina/session/StandardSession.java
+++ b/java/org/apache/catalina/session/StandardSession.java
@@ -21,6 +21,7 @@ import java.io.IOException;
 import java.io.NotSerializableException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.io.ObjectStreamException;
 import java.io.Serializable;
 import java.io.WriteAbortedException;
 import java.security.AccessController;
@@ -1582,24 +1583,52 @@ public class StandardSession implements HttpSession, Session, Serializable {
         throws ClassNotFoundException, IOException {
 
         // Deserialize the scalar instance variables (except Manager)
-        authType = null;        // Transient only
+        authType = null;        // Transient (may be set later)
         creationTime = ((Long) stream.readObject()).longValue();
         lastAccessedTime = ((Long) stream.readObject()).longValue();
         maxInactiveInterval = ((Integer) stream.readObject()).intValue();
         isNew = ((Boolean) stream.readObject()).booleanValue();
         isValid = ((Boolean) stream.readObject()).booleanValue();
         thisAccessedTime = ((Long) stream.readObject()).longValue();
-        principal = null;        // Transient only
+        principal = null;        // Transient (may be set later)
         //        setId((String) stream.readObject());
         id = (String) stream.readObject();
         if (manager.getContainer().getLogger().isDebugEnabled())
             manager.getContainer().getLogger().debug
                 ("readObject() loading session " + id);
 
+        // The next object read could either be the number of attributes (Integer) or the session's
+        // authType followed by a Principal object (not an Integer)
+        Object nextObject = stream.readObject();
+        if (!(nextObject instanceof Integer)) {
+            setAuthType((String) nextObject);
+            try {
+                setPrincipal((Principal) stream.readObject());
+            } catch (ClassNotFoundException e) {
+                String msg = sm.getString("standardSession.principalNotDeserializable", id);
+                if (manager.getContainer().getLogger().isDebugEnabled()) {
+                    manager.getContainer().getLogger().debug(msg, e);
+                } else {
+                    manager.getContainer().getLogger().warn(msg);
+                }
+                throw e;
+            } catch (ObjectStreamException e) {
+                String msg = sm.getString("standardSession.principalNotDeserializable", id);
+                if (manager.getContainer().getLogger().isDebugEnabled()) {
+                    manager.getContainer().getLogger().debug(msg, e);
+                } else {
+                    manager.getContainer().getLogger().warn(msg);
+                }
+                throw e;
+            }
+            // After that, the next object read should be the number of attributes (Integer)
+            nextObject = stream.readObject();
+        }
+
         // Deserialize the attribute count and attribute values
         if (attributes == null)
             attributes = new ConcurrentHashMap<String, Object>();
-        int n = ((Integer) stream.readObject()).intValue();
+        int n = ((Integer) nextObject).intValue();
         boolean isValidSave = isValid;
         isValid = true;
         for (int i = 0; i < n; i++) {
@@ -1677,6 +1706,28 @@ public class StandardSession implements HttpSession, Session, Serializable {
             manager.getContainer().getLogger().debug
                 ("writeObject() storing session " + id);
 
+        // Gather authentication information (if configured)
+        String sessionAuthType = null;
+        Principal sessionPrincipal = null;
+        if (isPersistAuthentication()) {
+            sessionAuthType = getAuthType();
+            sessionPrincipal = getPrincipal();
+            if (!(sessionPrincipal instanceof Serializable)) {
+                sessionPrincipal = null;
+                manager.getContainer().getLogger().warn(
+                        sm.getString("standardSession.principalNotSerializable", id));
+            }
+        }
+
+        // Write authentication information (may be null values)
+        stream.writeObject(sessionAuthType);
+        try {
+            stream.writeObject(sessionPrincipal);
+        } catch (NotSerializableException e) {
+            manager.getContainer().getLogger().warn(
+                    sm.getString("standardSession.principalNotSerializable", id), e);
+        }
+
         // Accumulate the names of serializable and non-serializable attributes
         String keys[] = keys();
         ArrayList<String> saveNames = new ArrayList<String>();
@@ -1711,6 +1762,18 @@ public class StandardSession implements HttpSession, Session, Serializable {
 
     }
 
+    /**
+     * Return whether authentication information shall be persisted or not.
+     *
+     * @return {@code true}, if authentication information shall be persisted;
+     *         {@code false} otherwise
+     */
+    private boolean isPersistAuthentication() {
+        if (manager instanceof ManagerBase) {
+            return ((ManagerBase) manager).getPersistAuthentication();
+        }
+        return false;
+    }
 
     /**
      * Exclude standard attributes that cannot be serialized.
diff --git a/java/org/apache/catalina/session/mbeans-descriptors.xml b/java/org/apache/catalina/session/mbeans-descriptors.xml
index f66a2c1..43af19c 100644
--- a/java/org/apache/catalina/session/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/session/mbeans-descriptors.xml
@@ -75,6 +75,10 @@
           description="Path name of the disk file in which active sessions"
                  type="java.lang.String"/>
 
+    <attribute   name="persistAuthentication"
+          description="Indicates whether sessions shall persist authentication information when being persisted (e.g. across application restarts)."
+                 type="boolean"/>
+
     <attribute   name="processExpiresFrequency"
           description="The frequency of the manager checks (expiration and passivation)"
                  type="int"/>
@@ -279,6 +283,10 @@
                  type="java.lang.String"
             writeable="false"/>
 
+    <attribute   name="persistAuthentication"
+          description="Indicates whether sessions shall persist authentication information when being backed up to the store (e.g. across application restarts)."
+                 type="boolean"/>
+
     <attribute   name="processExpiresFrequency"
           description="The frequency of the manager checks (expiration and passivation)"
                  type="int"/>
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index be66eac..89ce962 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -91,6 +91,12 @@
         The old class is still available but deprecated. Patch provided by Bernd
         Bohmann. (markt)
       </scode>
+      <add>
+        Add new attribute <code>persistAuthentication</code> to both
+        <code>StandardManager</code> and <code>PersistentManager</code> to
+        support authentication persistence. Patch provided by Carsten Klein.
+        (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Coyote">
diff --git a/webapps/docs/config/manager.xml b/webapps/docs/config/manager.xml
index 6286296..7f2d963 100644
--- a/webapps/docs/config/manager.xml
+++ b/webapps/docs/config/manager.xml
@@ -144,6 +144,40 @@
         disabled by setting this attribute to an empty string.</p>
       </attribute>
 
+      <attribute name="persistAuthentication" required="false">
+        <p>Should authentication information be included when session state is
+        preserved across application restarts? If <code>true</code>, the session's
+        authentication is preserved so that the session remains authenticated
+        after the application has been restarted. If not specified, the default
+        value of <code>false</code> will be used.<br />See
+        <a href="#Persistence_Across_Restarts">Persistence Across Restarts</a>
+        for more information.</p>
+
+        <p>Please note that the session's <code>Principal</code> class as well
+        as its descendant classes are all subject to the
+        <strong>sessionAttributeValueClassNameFilter</strong>. If such a filter
+        is specified or a <code>SecurityManager</code> is enabled, the names of
+        the <code>Principal</code> class and descendant classes must match that
+        filter pattern in order to be restored.</p>
+      </attribute>
+
+      <attribute name="persistAuthentication" required="false">
+        <p>Should authentication information be included when session state is
+        preserved across application restarts? If <code>true</code>, the session's
+        authentication is preserved so that the session remains authenticated
+        after the application has been restarted. If not specified, the default
+        value of <code>false</code> will be used.<br />See
+        <a href="#Persistence_Across_Restarts">Persistence Across Restarts</a>
+        for more information.</p>
+
+        <p>Please note that the session's <code>Principal</code> class as well
+        as its descendant classes are all subject to the
+        <strong>sessionAttributeValueClassNameFilter</strong>. If such a filter
+        is specified or a <code>SecurityManager</code> is enabled, the names of
+        the <code>Principal</code> class and descendant classes must match that
+        filter pattern in order to be restored.</p>
+      </attribute>
+
       <attribute name="processExpiresFrequency" required="false">
         <p>Frequency of the session expiration, and related manager operations.
         Manager operations will be done once for the specified amount of
@@ -200,7 +234,7 @@
         must fully match the pattern. If not specified, the default value of
         <code>null</code> will be used unless a <code>SecurityManager</code> is
         enabled in which case the default will be
-        <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p>
+        <code><nobr>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)|org\\.apache\\.catalina\\.realm\\.GenericPrincipal\\$SerializablePrincipal|\\[Ljava.lang.String;</nobr></code>.</p>
       </attribute>
 
       <attribute name="warnOnSessionAttributeFilterFailure" required="false">
@@ -271,6 +305,21 @@
         By default, this value is set to <code>-1</code>.</p>
       </attribute>
 
+      <attribute name="persistAuthentication" required="false">
+        <p>Should authentication information be included when sessions are
+        swapped out to persistent storage? If <code>true</code>, the session's
+        authentication is preserved so that the session remains authenticated
+        after being reloaded (swapped in) from persistent storage. If not 
+        specified, the default value of <code>false</code> will be used.</p>
+
+        <p>Please note that the session's <code>Principal</code> class as well
+        as its descendant classes are all subject to the
+        <strong>sessionAttributeValueClassNameFilter</strong>. If such a filter
+        is specified or a <code>SecurityManager</code> is enabled, the names of
+        the <code>Principal</code> class and descendant classes must match that
+        filter pattern in order to be restored.</p>
+      </attribute>
+
       <attribute name="processExpiresFrequency" required="false">
         <p>It is the same as described above for the
         <code>org.apache.catalina.session.StandardManager</code> class.
@@ -323,7 +372,7 @@
         must fully match the pattern. If not specified, the default value of
         <code>null</code> will be used unless a <code>SecurityManager</code> is
         enabled in which case the default will be
-        <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p>
+        <code><nobr>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)|org\\.apache\\.catalina\\.realm\\.GenericPrincipal\\$SerializablePrincipal|\\[Ljava.lang.String;</nobr></code>.</p>
       </attribute>
 
       <attribute name="warnOnSessionAttributeFilterFailure" required="false">
@@ -555,6 +604,15 @@
     including the <code>&lt;distributable&gt;</code> element in your web
     application deployment descriptor (<code>/WEB-INF/web.xml</code>).</p>
 
+    <p>Note that, if <strong>persistAuthentication</strong> is also set to
+    <code>true</code>, the <code>Principal</code> class present in the session
+    MUST also implement the <code>java.io.Serializable</code> interface in order
+    to make authentication persistence work properly. The actual type of that
+    <code>Principal</code> class is determined by the <a href="realm.html">
+    Realm</a> implementation used with the application. Tomcat's standard
+    <code>Principal</code> class instantiated by most of the Realms (except
+    <code>JAASRealm</code>) implements <code>java.io.Serializable</code>.</p>
+
     <p>The persistence across restarts provided by the
     <strong>StandardManager</strong> is a simpler implementation than that
     provided by the <strong>PersistentManager</strong>. If robust, production


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org