diff --git a/core/src/main/java/org/springframework/security/authentication/AbstractAuthenticationToken.java b/core/src/main/java/org/springframework/security/authentication/AbstractAuthenticationToken.java
index 19fd525ce4..0450e366cf 100644
--- a/core/src/main/java/org/springframework/security/authentication/AbstractAuthenticationToken.java
+++ b/core/src/main/java/org/springframework/security/authentication/AbstractAuthenticationToken.java
@@ -22,6 +22,7 @@ import java.util.Collections;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
@@ -34,7 +35,7 @@ import org.springframework.security.core.userdetails.UserDetails;
* @author Ben Alex
* @author Luke Taylor
*/
-public abstract class AbstractAuthenticationToken implements Authentication {
+public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
//~ Instance fields ================================================================================================
private Object details;
@@ -99,6 +100,22 @@ public abstract class AbstractAuthenticationToken implements Authentication {
this.details = details;
}
+ /**
+ * Checks the {@code credentials}, {@code principal} and {@code details} objects, invoking the
+ * {@code eraseCredentials} method on any which implement {@link CredentialsContainer}.
+ */
+ public void eraseCredentials() {
+ eraseSecret(getCredentials());
+ eraseSecret(getPrincipal());
+ eraseSecret(details);
+ }
+
+ private void eraseSecret(Object secret) {
+ if (secret instanceof CredentialsContainer) {
+ ((CredentialsContainer)secret).eraseCredentials();
+ }
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof AbstractAuthenticationToken)) {
@@ -174,7 +191,7 @@ public abstract class AbstractAuthenticationToken implements Authentication {
StringBuilder sb = new StringBuilder();
sb.append(super.toString()).append(": ");
sb.append("Principal: ").append(this.getPrincipal()).append("; ");
- sb.append("Password: [PROTECTED]; ");
+ sb.append("Credentials: [PROTECTED]; ");
sb.append("Authenticated: ").append(this.isAuthenticated()).append("; ");
sb.append("Details: ").append(this.getDetails()).append("; ");
diff --git a/core/src/main/java/org/springframework/security/authentication/ProviderManager.java b/core/src/main/java/org/springframework/security/authentication/ProviderManager.java
index 55badc8879..52b95b25d3 100644
--- a/core/src/main/java/org/springframework/security/authentication/ProviderManager.java
+++ b/core/src/main/java/org/springframework/security/authentication/ProviderManager.java
@@ -26,6 +26,7 @@ import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.util.Assert;
@@ -42,10 +43,17 @@ import org.springframework.util.Assert;
* AuthenticationException
, the last AuthenticationException
received will be used.
* If no provider returns a non-null response, or indicates it can even process an Authentication
,
* the ProviderManager
will throw a ProviderNotFoundException
.
+ * A parent {@code AuthenticationManager} can also be set, and this will also be tried if none of the configured
+ * providers can perform the authentication. This is intended to support namespace configuration options though and
+ * is not a feature that should normally be required.
*
* The exception to this process is when a provider throws an {@link AccountStatusException}, in which case no * further providers in the list will be queried. * + * Post-authentication, the credentials will be cleared from the returned {@code Authentication} object, if it + * implements the {@link CredentialsContainer} interface. This behaviour can be controlled by modifying the + * {@link #setEraseCredentialsAfterAuthentication(boolean) eraseCredentialsAfterAuthentication} property. + * *
* Authentication event publishing is delegated to the configured {@link AuthenticationEventPublisher} which defaults @@ -57,9 +65,10 @@ import org.springframework.util.Assert; * the <http> configuration, so you will receive events from the web part of your application automatically. *
* Note that the implementation also publishes authentication failure events when it obtains an authentication result
- * (or an exception) from the "parent" AuthenticationManager if one has been set. So in this situation, the
+ * (or an exception) from the "parent" {@code AuthenticationManager} if one has been set. So in this situation, the
* parent should not generally be configured to publish events or there will be duplicates.
*
+ *
* @author Ben Alex
* @author Luke Taylor
*
@@ -76,7 +85,7 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
private List
* The
+ * For internal framework use only. Users who are writing their own {@code AuthenticationProvider} implementations
+ * should create and return an appropriate {@code Authentication} object there, minus any sensitive data,
+ * rather than using this interface.
+ *
+ * @author Luke Taylor
+ * @since 3.0.3
+ */
+public interface CredentialsContainer {
+ void eraseCredentials();
+}
diff --git a/core/src/main/java/org/springframework/security/core/userdetails/User.java b/core/src/main/java/org/springframework/security/core/userdetails/User.java
index fcc5f7f285..d56ccd1220 100644
--- a/core/src/main/java/org/springframework/security/core/userdetails/User.java
+++ b/core/src/main/java/org/springframework/security/core/userdetails/User.java
@@ -24,6 +24,7 @@ import java.util.SortedSet;
import java.util.TreeSet;
import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.CredentialsContainer;
import org.springframework.util.Assert;
/**
@@ -41,9 +42,9 @@ import org.springframework.util.Assert;
* @author Ben Alex
* @author Luke Taylor
*/
-public class User implements UserDetails {
+public class User implements UserDetails, CredentialsContainer {
//~ Instance fields ================================================================================================
- private final String password;
+ private String password;
private final String username;
private final SetGrantedAuthority
s that apply.
*
* @author Ben Alex
+ * @author Luke Taylor
*/
-public class RememberMeAuthenticationToken extends AbstractAuthenticationToken implements Serializable {
+public class RememberMeAuthenticationToken extends AbstractAuthenticationToken {
//~ Instance fields ================================================================================================
private final Object principal;
diff --git a/core/src/main/java/org/springframework/security/authentication/UsernamePasswordAuthenticationToken.java b/core/src/main/java/org/springframework/security/authentication/UsernamePasswordAuthenticationToken.java
index 727eb7f11e..75fa959a24 100644
--- a/core/src/main/java/org/springframework/security/authentication/UsernamePasswordAuthenticationToken.java
+++ b/core/src/main/java/org/springframework/security/authentication/UsernamePasswordAuthenticationToken.java
@@ -22,8 +22,8 @@ import org.springframework.security.core.GrantedAuthority;
/**
- * An {@link org.springframework.security.core.Authentication} implementation that is designed for simple presentation of a
- * username and password.
+ * An {@link org.springframework.security.core.Authentication} implementation that is designed for simple presentation
+ * of a username and password.
* principal
and credentials
should be set with an Object
that provides
* the respective property via its Object.toString()
method. The simplest such Object
to use
@@ -34,8 +34,8 @@ import org.springframework.security.core.GrantedAuthority;
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
//~ Instance fields ================================================================================================
- private final Object credentials;
private final Object principal;
+ private Object credentials;
//~ Constructors ===================================================================================================
@@ -94,4 +94,10 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
super.setAuthenticated(false);
}
+
+ @Override
+ public void eraseCredentials() {
+ super.eraseCredentials();
+ credentials = null;
+ }
}
diff --git a/core/src/main/java/org/springframework/security/core/CredentialsContainer.java b/core/src/main/java/org/springframework/security/core/CredentialsContainer.java
new file mode 100644
index 0000000000..cb78215144
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/core/CredentialsContainer.java
@@ -0,0 +1,17 @@
+package org.springframework.security.core;
+
+/**
+ * Indicates that the implementing object contains sensitive data, which can be erased using the
+ * {@code eraseCredentials} method. Implementations are expected to invoke the method on any internal objects
+ * which may also implement this interface.
+ *