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:19:47 UTC
[tomcat] branch 9.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 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push:
new c91c16e Add an option to persist authentication information with the session
c91c16e is described below
commit c91c16ec3097977c989b3831e2bab1d74a71210b
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 | 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 e0a5487..62a8b49 100644
--- a/java/org/apache/catalina/session/LocalStrings.properties
+++ b/java/org/apache/catalina/session/LocalStrings.properties
@@ -84,6 +84,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 5e769c8..0cfc20a 100644
--- a/java/org/apache/catalina/session/ManagerBase.java
+++ b/java/org/apache/catalina/session/ManagerBase.java
@@ -197,6 +197,12 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
private boolean notifyAttributeListenerOnUnchangedValue = true;
+ /**
+ * Determines whether sessions managed by this manager shall persist (serialize)
+ * authentication information or not.
+ */
+ private boolean persistAuthentication = false;
+
// ------------------------------------------------------------ Constructors
@@ -204,8 +210,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);
}
}
@@ -543,10 +552,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 c8446f9..d3b3799 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;
@@ -1549,24 +1550,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++) {
@@ -1644,6 +1665,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();
List<String> saveNames = new ArrayList<>();
@@ -1678,6 +1721,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 c195bec..b73a230 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -79,6 +79,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 e067f45..adbd697 100644
--- a/webapps/docs/config/manager.xml
+++ b/webapps/docs/config/manager.xml
@@ -122,6 +122,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
@@ -178,7 +212,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">
@@ -249,6 +283,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.
@@ -301,7 +350,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">
@@ -546,6 +595,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-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org