diff --git a/core/src/main/java/org/springframework/security/config/ConfigUtils.java b/core/src/main/java/org/springframework/security/config/ConfigUtils.java
index 2e156b431d..ec0edd8864 100644
--- a/core/src/main/java/org/springframework/security/config/ConfigUtils.java
+++ b/core/src/main/java/org/springframework/security/config/ConfigUtils.java
@@ -4,12 +4,13 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
-import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.support.ManagedList;
+import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.AccessDecisionManager;
import org.springframework.security.AuthenticationManager;
import org.springframework.security.providers.ProviderManager;
+import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.vote.AffirmativeBased;
import org.springframework.security.vote.AuthenticatedVoter;
import org.springframework.security.vote.RoleVoter;
@@ -93,6 +94,20 @@ public abstract class ConfigUtils {
getAuthenticationManager(beanFactory));
}
+ static UserDetailsService getUserDetailsService(ConfigurableListableBeanFactory bf) {
+ Map services = bf.getBeansOfType(UserDetailsService.class);
+
+ if (services.size() == 0) {
+ throw new IllegalArgumentException("No UserDetailsService registered.");
+
+ } else if (services.size() > 1) {
+ throw new IllegalArgumentException("More than one UserDetailsService registered. Please" +
+ "use a specific Id in yur configuration");
+ }
+
+ return (UserDetailsService) services.values().toArray()[0];
+ }
+
private static AuthenticationManager getAuthenticationManager(ConfigurableListableBeanFactory bf) {
Map authManagers = bf.getBeansOfType(AuthenticationManager.class);
diff --git a/core/src/main/java/org/springframework/security/config/FormLoginBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/FormLoginBeanDefinitionParser.java
index fffadae86e..f0967d68b5 100644
--- a/core/src/main/java/org/springframework/security/config/FormLoginBeanDefinitionParser.java
+++ b/core/src/main/java/org/springframework/security/config/FormLoginBeanDefinitionParser.java
@@ -29,7 +29,7 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
private static final String LOGIN_PAGE_ATTRIBUTE = "loginPage";
private static final String FORM_LOGIN_TARGET_URL_ATTRIBUTE = "defaultTargetUrl";
- private static final String DEFAULT_FORM_LOGIN_TARGET_URL = "/index";
+ private static final String DEFAULT_FORM_LOGIN_TARGET_URL = "/";
private static final String FORM_LOGIN_AUTH_FAILURE_URL_ATTRIBUTE = "defaultTargetUrl";
// TODO: Change AbstractProcessingFilter to not need a failure URL and just write a failure message
@@ -38,8 +38,13 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
public BeanDefinition parse(Element elt, ParserContext parserContext) {
+ ConfigUtils.registerProviderManagerIfNecessary(parserContext);
+
BeanDefinition filterBean = createFilterBean(elt);
+ filterBean.getPropertyValues().addPropertyValue("authenticationManager",
+ new RuntimeBeanReference(ConfigUtils.DEFAULT_AUTH_MANAGER_ID));
+
BeanDefinitionBuilder entryPointBuilder =
BeanDefinitionBuilder.rootBeanDefinition(AuthenticationProcessingFilterEntryPoint.class);
@@ -90,8 +95,6 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
}
filterBuilder.addPropertyValue("authenticationFailureUrl", authenticationFailureUrl);
- // Set autowire to pick up the authentication manager.
- filterBuilder.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE);
return filterBuilder.getBeanDefinition();
}
diff --git a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java
index c4f3952ce4..e5c41496cd 100644
--- a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java
+++ b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java
@@ -21,7 +21,6 @@ import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
-import javax.servlet.Filter;
import java.util.*;
/**
@@ -44,6 +43,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
public static final String LOGOUT_ELEMENT = "logout";
public static final String FORM_LOGIN_ELEMENT = "form-login";
public static final String BASIC_AUTH_ELEMENT = "http-basic";
+ public static final String REMEMBER_ME_ELEMENT = "remember-me";
static final String PATH_PATTERN_ATTRIBUTE = "pattern";
static final String PATTERN_TYPE_ATTRIBUTE = "pathType";
@@ -120,6 +120,12 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
new BasicAuthenticationBeanDefinitionParser().parse(basicAuthElt, parserContext);
}
+ Element rememberMeElt = DomUtils.getChildElementByTagName(element, REMEMBER_ME_ELEMENT);
+
+ if (rememberMeElt != null) {
+ new RememberMeBeanDefinitionParser().parse(rememberMeElt, parserContext);
+ }
+
registry.registerBeanDefinition(DEFAULT_FILTER_CHAIN_PROXY_ID, filterChainProxy);
registry.registerBeanDefinition(DEFAULT_HTTP_SESSION_FILTER_ID, httpSCIF);
registry.registerBeanDefinition(DEFAULT_EXCEPTION_TRANSLATION_FILTER_ID,
diff --git a/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java b/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java
index 42eca561c2..b2452027a8 100644
--- a/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java
+++ b/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java
@@ -3,14 +3,17 @@ package org.springframework.security.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
-import org.springframework.security.AuthenticationManager;
+import org.springframework.security.concurrent.ConcurrentSessionFilter;
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
+import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.AuthenticationEntryPoint;
+import org.springframework.security.ui.rememberme.RememberMeServices;
import org.springframework.security.util.FilterChainProxy;
import org.springframework.util.Assert;
@@ -38,11 +41,55 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
ConfigUtils.configureSecurityInterceptor(beanFactory, securityInterceptor);
+ configureRememberMeSerices(beanFactory);
+
configureAuthenticationEntryPoint(beanFactory);
+ configureAuthenticationFilter(beanFactory);
+
configureFilterChain(beanFactory);
}
+ private void configureRememberMeSerices(ConfigurableListableBeanFactory beanFactory) {
+ try {
+ BeanDefinition rememberMeServices =
+ beanFactory.getBeanDefinition(RememberMeBeanDefinitionParser.DEFAULT_REMEMBER_ME_SERVICES_ID);
+ rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService",
+ ConfigUtils.getUserDetailsService(beanFactory));
+
+ BeanDefinition logoutFilter =
+ beanFactory.getBeanDefinition(HttpSecurityBeanDefinitionParser.DEFAULT_FILTER_SECURITY_INTERCEPTOR_ID);
+
+ } catch (NoSuchBeanDefinitionException e) {
+ // ignore
+ }
+ }
+
+ /**
+ * Sets the authentication manager, (and remember-me services, if required) on any instances of
+ * AbstractProcessingFilter
+ */
+ private void configureAuthenticationFilter(ConfigurableListableBeanFactory beanFactory) {
+ Map beans = beanFactory.getBeansOfType(RememberMeServices.class);
+
+ RememberMeServices rememberMeServices = null;
+
+ if (beans.size() > 0) {
+ rememberMeServices = (RememberMeServices) beans.values().toArray()[0];
+ }
+
+ Iterator authFilters = beanFactory.getBeansOfType(AbstractProcessingFilter.class).values().iterator();
+
+ while (authFilters.hasNext()) {
+ AbstractProcessingFilter filter = (AbstractProcessingFilter) authFilters.next();
+
+ if (rememberMeServices != null) {
+ logger.info("Using RememberMeServices " + rememberMeServices + " with filter " + filter);
+ filter.setRememberMeServices(rememberMeServices);
+ }
+ }
+ }
+
/**
* Selects the entry point that should be used in ExceptionTranslationFilter. Strategy is
*
@@ -52,7 +99,6 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
*
throw an exception (for now). TODO: Examine additional beans and types and make decision
*
*
- * @param beanFactory
*/
private void configureAuthenticationEntryPoint(ConfigurableListableBeanFactory beanFactory) {
logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter");
@@ -90,6 +136,15 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
filterMap.put(allUrlsMatch, defaultFilterChain);
filterChainProxy.setFilterChainMap(filterMap);
+
+ Map sessionFilters = beanFactory.getBeansOfType(ConcurrentSessionFilter.class);
+
+ if (!sessionFilters.isEmpty()) {
+ logger.info("Concurrent session filter in use, setting 'forceEagerSessionCreation' to true");
+ HttpSessionContextIntegrationFilter scif = (HttpSessionContextIntegrationFilter)
+ beanFactory.getBean(HttpSecurityBeanDefinitionParser.DEFAULT_HTTP_SESSION_FILTER_ID);
+ scif.setForceEagerSessionCreation(true);
+ }
}
private List orderFilters(ConfigurableListableBeanFactory beanFactory) {
diff --git a/core/src/main/java/org/springframework/security/config/RememberMeBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/RememberMeBeanDefinitionParser.java
new file mode 100644
index 0000000000..f2fdd31934
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/config/RememberMeBeanDefinitionParser.java
@@ -0,0 +1,72 @@
+package org.springframework.security.config;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.support.ManagedList;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.security.ui.rememberme.JdbcTokenRepositoryImpl;
+import org.springframework.security.ui.rememberme.PersistentTokenBasedRememberMeServices;
+import org.springframework.security.ui.rememberme.RememberMeProcessingFilter;
+import org.springframework.security.ui.rememberme.TokenBasedRememberMeServices;
+import org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Element;
+
+/**
+ * @author Luke Taylor
+ * @version $Id$
+ */
+public class RememberMeBeanDefinitionParser implements BeanDefinitionParser {
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ public static final String DEFAULT_REMEMBER_ME_FILTER_ID = "_rememberMeFilter";
+ public static final String DEFAULT_REMEMBER_ME_SERVICES_ID = "_rememberMeServices";
+
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+ BeanDefinition filter = new RootBeanDefinition(RememberMeProcessingFilter.class);
+ BeanDefinition services = new RootBeanDefinition(PersistentTokenBasedRememberMeServices.class);
+
+ filter.getPropertyValues().addPropertyValue("authenticationManager",
+ new RuntimeBeanReference(ConfigUtils.DEFAULT_AUTH_MANAGER_ID));
+
+ String tokenRepository = element.getAttribute("tokenRepository");
+ String dataSource = element.getAttribute("dataSource");
+
+ if (StringUtils.hasText(tokenRepository)) {
+ if (StringUtils.hasText(dataSource)) {
+ throw new SecurityConfigurationException("Specify tokenRepository or dataSource but not both");
+ }
+
+ services.getPropertyValues().addPropertyValue("tokenRepository", new RuntimeBeanReference(tokenRepository));
+
+ } else if (StringUtils.hasText(dataSource)) {
+ BeanDefinition tokenRepo = new RootBeanDefinition(JdbcTokenRepositoryImpl.class);
+ tokenRepo.getPropertyValues().addPropertyValue("dataSource", new RuntimeBeanReference(dataSource));
+ } else {
+ // Not persistent
+ services = new RootBeanDefinition(TokenBasedRememberMeServices.class);
+ }
+
+ String key = element.getAttribute("key");
+ services.getPropertyValues().addPropertyValue("key", key);
+
+ BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
+ BeanDefinition provider = new RootBeanDefinition(RememberMeAuthenticationProvider.class);
+ provider.getPropertyValues().addPropertyValue("key", key);
+
+ ManagedList providers = (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue();
+ providers.add(provider);
+
+ filter.getPropertyValues().addPropertyValue("rememberMeServices",
+ new RuntimeBeanReference(DEFAULT_REMEMBER_ME_SERVICES_ID));
+
+ parserContext.getRegistry().registerBeanDefinition(DEFAULT_REMEMBER_ME_SERVICES_ID, services);
+ parserContext.getRegistry().registerBeanDefinition(DEFAULT_REMEMBER_ME_FILTER_ID, filter);
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/springframework/security/ui/rememberme/AbstractRememberMeServices.java b/core/src/main/java/org/springframework/security/ui/rememberme/AbstractRememberMeServices.java
index 22545e1ffb..946744dd16 100644
--- a/core/src/main/java/org/springframework/security/ui/rememberme/AbstractRememberMeServices.java
+++ b/core/src/main/java/org/springframework/security/ui/rememberme/AbstractRememberMeServices.java
@@ -3,17 +3,20 @@ package org.springframework.security.ui.rememberme;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.Authentication;
import org.springframework.security.SpringSecurityMessageSource;
import org.springframework.security.providers.rememberme.RememberMeAuthenticationToken;
import org.springframework.security.ui.AuthenticationDetailsSource;
import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
+import org.springframework.security.ui.logout.LogoutHandler;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.userdetails.UsernameNotFoundException;
+import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.ServletRequestUtils;
-import org.springframework.context.support.MessageSourceAccessor;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@@ -25,7 +28,7 @@ import javax.servlet.http.HttpServletResponse;
* @author Luke Taylor
* @version $Id$
*/
-public abstract class AbstractRememberMeServices implements RememberMeServices {
+public abstract class AbstractRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler {
protected final Log logger = LogFactory.getLog(getClass());
@@ -42,7 +45,14 @@ public abstract class AbstractRememberMeServices implements RememberMeServices {
private String parameter = DEFAULT_PARAMETER;
private boolean alwaysRemember;
private String key;
- private long tokenValiditySeconds = 1209600; // 14 days
+ private int tokenValiditySeconds = 1209600; // 14 days
+
+ public void afterPropertiesSet() throws Exception {
+ Assert.hasLength(key);
+ Assert.hasLength(parameter);
+ Assert.hasLength(cookieName);
+ Assert.notNull(userDetailsService);
+ }
/**
* Template implementation which locates the Spring Security cookie, decodes it into
@@ -261,13 +271,21 @@ public abstract class AbstractRememberMeServices implements RememberMeServices {
return cookie;
}
- protected Cookie makeValidCookie(String value, HttpServletRequest request, long maxAge) {
+ protected Cookie makeValidCookie(String value, HttpServletRequest request, int maxAge) {
Cookie cookie = new Cookie(cookieName, value);
- cookie.setMaxAge(new Long(maxAge).intValue());
+ cookie.setMaxAge(maxAge);
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
return cookie;
}
+
+ public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
+ if (logger.isDebugEnabled()) {
+ logger.debug( "Logout of user "
+ + (authentication == null ? "Unknown" : authentication.getName()));
+ }
+ cancelCookie(request, response);
+ }
public void setCookieName(String cookieName) {
this.cookieName = cookieName;
@@ -281,6 +299,10 @@ public abstract class AbstractRememberMeServices implements RememberMeServices {
this.parameter = parameter;
}
+ public String getParameter() {
+ return parameter;
+ }
+
protected UserDetailsService getUserDetailsService() {
return userDetailsService;
}
@@ -293,11 +315,11 @@ public abstract class AbstractRememberMeServices implements RememberMeServices {
this.key = key;
}
- public void setTokenValiditySeconds(long tokenValiditySeconds) {
+ public void setTokenValiditySeconds(int tokenValiditySeconds) {
this.tokenValiditySeconds = tokenValiditySeconds;
}
- public long getTokenValiditySeconds() {
+ public int getTokenValiditySeconds() {
return tokenValiditySeconds;
}
diff --git a/core/src/main/java/org/springframework/security/ui/rememberme/JdbcTokenRepositoryImpl.java b/core/src/main/java/org/springframework/security/ui/rememberme/JdbcTokenRepositoryImpl.java
new file mode 100644
index 0000000000..69b868f55f
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/ui/rememberme/JdbcTokenRepositoryImpl.java
@@ -0,0 +1,34 @@
+package org.springframework.security.ui.rememberme;
+
+import org.springframework.jdbc.core.support.JdbcDaoSupport;
+
+/**
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ */
+public class JdbcTokenRepositoryImpl extends JdbcDaoSupport implements PersistentTokenRepository {
+ //~ Static fields/initializers =====================================================================================
+ public static final String DEF_TOKEN_BY_SERIES_QUERY =
+ "select username,series,token from persistent_logins where series = ?";
+ public static final String DEF_INSERT_TOKEN_STATEMENT =
+ "insert into persistent_logins (username,series,token) values(?,?,?)";
+ public static final String DEF_REMOVE_USER_TOKENS_STATEMENT =
+ "delete from persistent_logins where username = ?";
+
+ //~ Instance fields ================================================================================================
+
+ private String tokensBySeriesQuery = DEF_TOKEN_BY_SERIES_QUERY;
+ private String insertTokenStatement = DEF_INSERT_TOKEN_STATEMENT;
+ private String removeUserTokensStatement = DEF_REMOVE_USER_TOKENS_STATEMENT;
+
+ public void saveToken(PersistentRememberMeToken token) {
+ }
+
+ public PersistentRememberMeToken getTokenForSeries(String seriesId) {
+ return null;
+ }
+
+ public void removeAllTokens(String username) {
+ }
+}
diff --git a/core/src/main/java/org/springframework/security/ui/rememberme/PersistentTokenBasedRememberMeServices.java b/core/src/main/java/org/springframework/security/ui/rememberme/PersistentTokenBasedRememberMeServices.java
index 9bc89099a8..894ddb44ce 100644
--- a/core/src/main/java/org/springframework/security/ui/rememberme/PersistentTokenBasedRememberMeServices.java
+++ b/core/src/main/java/org/springframework/security/ui/rememberme/PersistentTokenBasedRememberMeServices.java
@@ -109,7 +109,10 @@ public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeSe
}
private PersistentRememberMeToken createNewToken(String username, String series) {
- logger.debug("Creating new persistent login token for user " + username);
+ if (logger.isDebugEnabled()) {
+ logger.debug(series == null ? "Creating new" : "Renewing" +
+ " persistent login token for user " + username);
+ }
if (series == null) {
byte[] newSeries = new byte[seriesLength];
@@ -131,8 +134,7 @@ public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeSe
private void addCookie(PersistentRememberMeToken token, HttpServletRequest request, HttpServletResponse response) {
String cookieValue = encodeCookie(new String[] {token.getSeries(), token.getTokenValue()});
- long maxAge = System.currentTimeMillis() + getTokenValiditySeconds() * 1000;
- response.addCookie(makeValidCookie(cookieValue, request, maxAge));
+ response.addCookie(makeValidCookie(cookieValue, request, getTokenValiditySeconds()));
}
public void setTokenRepository(PersistentTokenRepository tokenRepository) {
diff --git a/core/src/main/java/org/springframework/security/ui/webapp/DefaultLoginPageGeneratingFilter.java b/core/src/main/java/org/springframework/security/ui/webapp/DefaultLoginPageGeneratingFilter.java
index b1d010b981..7a0b422ecf 100644
--- a/core/src/main/java/org/springframework/security/ui/webapp/DefaultLoginPageGeneratingFilter.java
+++ b/core/src/main/java/org/springframework/security/ui/webapp/DefaultLoginPageGeneratingFilter.java
@@ -4,6 +4,7 @@ import org.springframework.security.AuthenticationException;
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.SpringSecurityFilter;
+import org.springframework.security.ui.rememberme.AbstractRememberMeServices;
import org.springframework.security.ui.rememberme.TokenBasedRememberMeServices;
import org.springframework.util.StringUtils;
@@ -35,8 +36,8 @@ public class DefaultLoginPageGeneratingFilter extends SpringSecurityFilter {
usernameParameter = authFilter.getUsernameParameter();
passwordParameter = authFilter.getPasswordParameter();
- if (authFilter.getRememberMeServices() instanceof TokenBasedRememberMeServices) {
- rememberMeParameter = ((TokenBasedRememberMeServices)authFilter.getRememberMeServices()).getParameter();
+ if (authFilter.getRememberMeServices() instanceof AbstractRememberMeServices) {
+ rememberMeParameter = ((AbstractRememberMeServices)authFilter.getRememberMeServices()).getParameter();
}
}
diff --git a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc
index 79e2e8f300..21ad670c9e 100644
--- a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc
+++ b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc
@@ -48,7 +48,7 @@ protect.attlist &=
http =
## Container element for HTTP security configuration
- element http {http.attlist, intercept-url+, form-login?, http-basic?, logout?, concurrent-session-control? }
+ element http {http.attlist, (intercept-url+ & form-login? & http-basic? & logout? & concurrent-session-control? & remember-me?) }
http.attlist &=
## Controls the eagerness with which an HTTP session is created.
[ a:defaultValue = "ifRequired" ] attribute createSession {"ifRequired" | "always" | "never" }?
@@ -135,6 +135,13 @@ concurrent-sessions.attlist &=
concurrent-sessions.attlist &=
attribute exceptionIfMaximumExceeded {"true" | "false"}?
+remember-me =
+ element remember-me {remember-me.attlist}
+remember-me.attlist &=
+ attribute key {xsd:string}
+remember-me.attlist &=
+ (attribute tokenRepository {xsd:string} | attribute datasource {xsd:string})?
+
authentication-provider =
element authentication-provider {authentication-provider.attlist, (user-service | jdbc-user-service)}
authentication-provider.attlist &= empty
@@ -144,7 +151,6 @@ user-service =
user-service.attlist &=
attribute properties {xsd:string}*
-
user =
element user {user.attlist, empty}
user.attlist &=
diff --git a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd
index adfb1a6b03..8fbac3a4ae 100644
--- a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd
+++ b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd
@@ -72,13 +72,14 @@
Container element for HTTP security configuration
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -241,6 +242,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java b/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java
index d34faaa6a6..fddfaa923c 100644
--- a/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java
+++ b/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java
@@ -1,20 +1,22 @@
package org.springframework.security.config;
-import org.junit.AfterClass;
-import static org.junit.Assert.assertTrue;
-import org.junit.BeforeClass;
-import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.concurrent.ConcurrentSessionFilter;
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
import org.springframework.security.intercept.web.FilterSecurityInterceptor;
import org.springframework.security.ui.ExceptionTranslationFilter;
+import org.springframework.security.ui.rememberme.RememberMeProcessingFilter;
import org.springframework.security.ui.basicauth.BasicProcessingFilter;
import org.springframework.security.ui.logout.LogoutFilter;
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter;
import org.springframework.security.util.FilterChainProxy;
+import org.junit.AfterClass;
+import static org.junit.Assert.*;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
import java.util.Iterator;
import java.util.List;
@@ -54,7 +56,7 @@ public class HttpSecurityBeanDefinitionParserTests {
List filterList = filterChainProxy.getFilters("/someurl");
- assertTrue("Expected 8 filters in chain", filterList.size() == 8);
+ assertEquals("Expected 9 filters in chain", 9, filterList.size());
Iterator filters = filterList.iterator();
@@ -64,6 +66,7 @@ public class HttpSecurityBeanDefinitionParserTests {
assertTrue(filters.next() instanceof AuthenticationProcessingFilter);
assertTrue(filters.next() instanceof DefaultLoginPageGeneratingFilter);
assertTrue(filters.next() instanceof BasicProcessingFilter);
+ assertTrue(filters.next() instanceof RememberMeProcessingFilter);
assertTrue(filters.next() instanceof ExceptionTranslationFilter);
assertTrue(filters.next() instanceof FilterSecurityInterceptor);
}
diff --git a/core/src/test/resources/org/springframework/security/config/http-security.xml b/core/src/test/resources/org/springframework/security/config/http-security.xml
index ba978e5ff5..60c5933b80 100644
--- a/core/src/test/resources/org/springframework/security/config/http-security.xml
+++ b/core/src/test/resources/org/springframework/security/config/http-security.xml
@@ -7,20 +7,22 @@
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
-
+
-
+
-
+
+
+
@@ -29,4 +31,9 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/tutorial/src/main/webapp/WEB-INF/applicationContext-security-ns.xml b/samples/tutorial/src/main/webapp/WEB-INF/applicationContext-security-ns.xml
index 9134b5d509..91452ea7ef 100644
--- a/samples/tutorial/src/main/webapp/WEB-INF/applicationContext-security-ns.xml
+++ b/samples/tutorial/src/main/webapp/WEB-INF/applicationContext-security-ns.xml
@@ -22,8 +22,10 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
+
+