This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 8.5.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/8.5.x by this push: new 33bd08b Add an option to persist authentication information with the session 33bd08b is described below commit 33bd08b9617d81ca99758412ea405ca2994106da Author: Carsten Klein <c.kl...@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 | 61 +++++++++++++++++++-- .../apache/catalina/session/mbeans-descriptors.xml | 8 +++ webapps/docs/changelog.xml | 6 +++ webapps/docs/config/manager.xml | 62 +++++++++++++++++++++- 6 files changed, 169 insertions(+), 7 deletions(-) diff --git a/java/org/apache/catalina/session/LocalStrings.properties b/java/org/apache/catalina/session/LocalStrings.properties index 51b7a01..289b272 100644 --- a/java/org/apache/catalina/session/LocalStrings.properties +++ b/java/org/apache/catalina/session/LocalStrings.properties @@ -79,6 +79,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.sessionEvent=Session event listener threw exception standardSession.setAttribute.iae=setAttribute: Non-serializable attribute [{0}] diff --git a/java/org/apache/catalina/session/ManagerBase.java b/java/org/apache/catalina/session/ManagerBase.java index 9759e27..51b4f09 100644 --- a/java/org/apache/catalina/session/ManagerBase.java +++ b/java/org/apache/catalina/session/ManagerBase.java @@ -192,6 +192,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 @@ -199,8 +205,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); } } @@ -513,10 +522,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 3c17587..557787e 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; @@ -1554,24 +1555,44 @@ 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.getContext().getLogger().isDebugEnabled()) manager.getContext().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 | ObjectStreamException e) { + String msg = sm.getString("standardSession.principalNotDeserializable", id); + if (manager.getContext().getLogger().isDebugEnabled()) { + manager.getContext().getLogger().debug(msg, e); + } else { + manager.getContext().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<>(); - int n = ((Integer) stream.readObject()).intValue(); + int n = ((Integer) nextObject).intValue(); boolean isValidSave = isValid; isValid = true; for (int i = 0; i < n; i++) { @@ -1649,6 +1670,28 @@ public class StandardSession implements HttpSession, Session, Serializable { manager.getContext().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.getContext().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.getContext().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<>(); @@ -1683,6 +1726,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; + } /** * Should the given session attribute be excluded? This implementation diff --git a/java/org/apache/catalina/session/mbeans-descriptors.xml b/java/org/apache/catalina/session/mbeans-descriptors.xml index ccec4d6..c732672 100644 --- a/java/org/apache/catalina/session/mbeans-descriptors.xml +++ b/java/org/apache/catalina/session/mbeans-descriptors.xml @@ -65,6 +65,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"/> @@ -256,6 +260,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 5bad4c0..c9240ee 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -85,6 +85,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 989b6ef..616c508 100644 --- a/webapps/docs/config/manager.xml +++ b/webapps/docs/config/manager.xml @@ -105,6 +105,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 @@ -161,7 +195,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"> @@ -232,6 +266,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. @@ -284,7 +333,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"> @@ -529,6 +578,15 @@ including the <code><distributable></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-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org