mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-29 15:22:15 +00:00
SEC-582: Namespace configuration implementation for remember-me support.
This commit is contained in:
parent
b868143fb1
commit
9f2bc9a842
@ -4,12 +4,13 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
|||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
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.ManagedList;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.beans.factory.xml.ParserContext;
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
import org.springframework.security.AccessDecisionManager;
|
import org.springframework.security.AccessDecisionManager;
|
||||||
import org.springframework.security.AuthenticationManager;
|
import org.springframework.security.AuthenticationManager;
|
||||||
import org.springframework.security.providers.ProviderManager;
|
import org.springframework.security.providers.ProviderManager;
|
||||||
|
import org.springframework.security.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.vote.AffirmativeBased;
|
import org.springframework.security.vote.AffirmativeBased;
|
||||||
import org.springframework.security.vote.AuthenticatedVoter;
|
import org.springframework.security.vote.AuthenticatedVoter;
|
||||||
import org.springframework.security.vote.RoleVoter;
|
import org.springframework.security.vote.RoleVoter;
|
||||||
@ -93,6 +94,20 @@ public abstract class ConfigUtils {
|
|||||||
getAuthenticationManager(beanFactory));
|
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) {
|
private static AuthenticationManager getAuthenticationManager(ConfigurableListableBeanFactory bf) {
|
||||||
Map authManagers = bf.getBeansOfType(AuthenticationManager.class);
|
Map authManagers = bf.getBeansOfType(AuthenticationManager.class);
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
private static final String LOGIN_PAGE_ATTRIBUTE = "loginPage";
|
private static final String LOGIN_PAGE_ATTRIBUTE = "loginPage";
|
||||||
|
|
||||||
private static final String FORM_LOGIN_TARGET_URL_ATTRIBUTE = "defaultTargetUrl";
|
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";
|
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
|
// 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) {
|
public BeanDefinition parse(Element elt, ParserContext parserContext) {
|
||||||
|
ConfigUtils.registerProviderManagerIfNecessary(parserContext);
|
||||||
|
|
||||||
BeanDefinition filterBean = createFilterBean(elt);
|
BeanDefinition filterBean = createFilterBean(elt);
|
||||||
|
|
||||||
|
filterBean.getPropertyValues().addPropertyValue("authenticationManager",
|
||||||
|
new RuntimeBeanReference(ConfigUtils.DEFAULT_AUTH_MANAGER_ID));
|
||||||
|
|
||||||
BeanDefinitionBuilder entryPointBuilder =
|
BeanDefinitionBuilder entryPointBuilder =
|
||||||
BeanDefinitionBuilder.rootBeanDefinition(AuthenticationProcessingFilterEntryPoint.class);
|
BeanDefinitionBuilder.rootBeanDefinition(AuthenticationProcessingFilterEntryPoint.class);
|
||||||
|
|
||||||
@ -90,8 +95,6 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
filterBuilder.addPropertyValue("authenticationFailureUrl", authenticationFailureUrl);
|
filterBuilder.addPropertyValue("authenticationFailureUrl", authenticationFailureUrl);
|
||||||
// Set autowire to pick up the authentication manager.
|
|
||||||
filterBuilder.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE);
|
|
||||||
|
|
||||||
return filterBuilder.getBeanDefinition();
|
return filterBuilder.getBeanDefinition();
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import org.springframework.util.StringUtils;
|
|||||||
import org.springframework.util.xml.DomUtils;
|
import org.springframework.util.xml.DomUtils;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
import javax.servlet.Filter;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,6 +43,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
public static final String LOGOUT_ELEMENT = "logout";
|
public static final String LOGOUT_ELEMENT = "logout";
|
||||||
public static final String FORM_LOGIN_ELEMENT = "form-login";
|
public static final String FORM_LOGIN_ELEMENT = "form-login";
|
||||||
public static final String BASIC_AUTH_ELEMENT = "http-basic";
|
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 PATH_PATTERN_ATTRIBUTE = "pattern";
|
||||||
static final String PATTERN_TYPE_ATTRIBUTE = "pathType";
|
static final String PATTERN_TYPE_ATTRIBUTE = "pathType";
|
||||||
@ -120,6 +120,12 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
new BasicAuthenticationBeanDefinitionParser().parse(basicAuthElt, parserContext);
|
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_FILTER_CHAIN_PROXY_ID, filterChainProxy);
|
||||||
registry.registerBeanDefinition(DEFAULT_HTTP_SESSION_FILTER_ID, httpSCIF);
|
registry.registerBeanDefinition(DEFAULT_HTTP_SESSION_FILTER_ID, httpSCIF);
|
||||||
registry.registerBeanDefinition(DEFAULT_EXCEPTION_TRANSLATION_FILTER_ID,
|
registry.registerBeanDefinition(DEFAULT_EXCEPTION_TRANSLATION_FILTER_ID,
|
||||||
|
@ -3,14 +3,17 @@ package org.springframework.security.config;
|
|||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.core.OrderComparator;
|
import org.springframework.core.OrderComparator;
|
||||||
import org.springframework.core.Ordered;
|
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.context.HttpSessionContextIntegrationFilter;
|
||||||
|
import org.springframework.security.ui.AbstractProcessingFilter;
|
||||||
import org.springframework.security.ui.AuthenticationEntryPoint;
|
import org.springframework.security.ui.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.security.ui.rememberme.RememberMeServices;
|
||||||
import org.springframework.security.util.FilterChainProxy;
|
import org.springframework.security.util.FilterChainProxy;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@ -38,11 +41,55 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
|
|||||||
|
|
||||||
ConfigUtils.configureSecurityInterceptor(beanFactory, securityInterceptor);
|
ConfigUtils.configureSecurityInterceptor(beanFactory, securityInterceptor);
|
||||||
|
|
||||||
|
configureRememberMeSerices(beanFactory);
|
||||||
|
|
||||||
configureAuthenticationEntryPoint(beanFactory);
|
configureAuthenticationEntryPoint(beanFactory);
|
||||||
|
|
||||||
|
configureAuthenticationFilter(beanFactory);
|
||||||
|
|
||||||
configureFilterChain(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
|
* Selects the entry point that should be used in ExceptionTranslationFilter. Strategy is
|
||||||
*
|
*
|
||||||
@ -52,7 +99,6 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
|
|||||||
* <li>throw an exception (for now). TODO: Examine additional beans and types and make decision</li>
|
* <li>throw an exception (for now). TODO: Examine additional beans and types and make decision</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
* @param beanFactory
|
|
||||||
*/
|
*/
|
||||||
private void configureAuthenticationEntryPoint(ConfigurableListableBeanFactory beanFactory) {
|
private void configureAuthenticationEntryPoint(ConfigurableListableBeanFactory beanFactory) {
|
||||||
logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter");
|
logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter");
|
||||||
@ -90,6 +136,15 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
|
|||||||
filterMap.put(allUrlsMatch, defaultFilterChain);
|
filterMap.put(allUrlsMatch, defaultFilterChain);
|
||||||
|
|
||||||
filterChainProxy.setFilterChainMap(filterMap);
|
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) {
|
private List orderFilters(ConfigurableListableBeanFactory beanFactory) {
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -3,17 +3,20 @@ package org.springframework.security.ui.rememberme;
|
|||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
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.Authentication;
|
||||||
import org.springframework.security.SpringSecurityMessageSource;
|
import org.springframework.security.SpringSecurityMessageSource;
|
||||||
import org.springframework.security.providers.rememberme.RememberMeAuthenticationToken;
|
import org.springframework.security.providers.rememberme.RememberMeAuthenticationToken;
|
||||||
import org.springframework.security.ui.AuthenticationDetailsSource;
|
import org.springframework.security.ui.AuthenticationDetailsSource;
|
||||||
import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
|
import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
|
||||||
|
import org.springframework.security.ui.logout.LogoutHandler;
|
||||||
import org.springframework.security.userdetails.UserDetails;
|
import org.springframework.security.userdetails.UserDetails;
|
||||||
import org.springframework.security.userdetails.UserDetailsService;
|
import org.springframework.security.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.userdetails.UsernameNotFoundException;
|
import org.springframework.security.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.ServletRequestUtils;
|
import org.springframework.web.bind.ServletRequestUtils;
|
||||||
import org.springframework.context.support.MessageSourceAccessor;
|
|
||||||
|
|
||||||
import javax.servlet.http.Cookie;
|
import javax.servlet.http.Cookie;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@ -25,7 +28,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractRememberMeServices implements RememberMeServices {
|
public abstract class AbstractRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler {
|
||||||
|
|
||||||
protected final Log logger = LogFactory.getLog(getClass());
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
@ -42,7 +45,14 @@ public abstract class AbstractRememberMeServices implements RememberMeServices {
|
|||||||
private String parameter = DEFAULT_PARAMETER;
|
private String parameter = DEFAULT_PARAMETER;
|
||||||
private boolean alwaysRemember;
|
private boolean alwaysRemember;
|
||||||
private String key;
|
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
|
* Template implementation which locates the Spring Security cookie, decodes it into
|
||||||
@ -261,14 +271,22 @@ public abstract class AbstractRememberMeServices implements RememberMeServices {
|
|||||||
return cookie;
|
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 cookie = new Cookie(cookieName, value);
|
||||||
cookie.setMaxAge(new Long(maxAge).intValue());
|
cookie.setMaxAge(maxAge);
|
||||||
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
|
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
|
||||||
|
|
||||||
return cookie;
|
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) {
|
public void setCookieName(String cookieName) {
|
||||||
this.cookieName = cookieName;
|
this.cookieName = cookieName;
|
||||||
}
|
}
|
||||||
@ -281,6 +299,10 @@ public abstract class AbstractRememberMeServices implements RememberMeServices {
|
|||||||
this.parameter = parameter;
|
this.parameter = parameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getParameter() {
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
|
||||||
protected UserDetailsService getUserDetailsService() {
|
protected UserDetailsService getUserDetailsService() {
|
||||||
return userDetailsService;
|
return userDetailsService;
|
||||||
}
|
}
|
||||||
@ -293,11 +315,11 @@ public abstract class AbstractRememberMeServices implements RememberMeServices {
|
|||||||
this.key = key;
|
this.key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTokenValiditySeconds(long tokenValiditySeconds) {
|
public void setTokenValiditySeconds(int tokenValiditySeconds) {
|
||||||
this.tokenValiditySeconds = tokenValiditySeconds;
|
this.tokenValiditySeconds = tokenValiditySeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTokenValiditySeconds() {
|
public int getTokenValiditySeconds() {
|
||||||
return tokenValiditySeconds;
|
return tokenValiditySeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
}
|
||||||
|
}
|
@ -109,7 +109,10 @@ public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private PersistentRememberMeToken createNewToken(String username, String series) {
|
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) {
|
if (series == null) {
|
||||||
byte[] newSeries = new byte[seriesLength];
|
byte[] newSeries = new byte[seriesLength];
|
||||||
@ -131,8 +134,7 @@ public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeSe
|
|||||||
|
|
||||||
private void addCookie(PersistentRememberMeToken token, HttpServletRequest request, HttpServletResponse response) {
|
private void addCookie(PersistentRememberMeToken token, HttpServletRequest request, HttpServletResponse response) {
|
||||||
String cookieValue = encodeCookie(new String[] {token.getSeries(), token.getTokenValue()});
|
String cookieValue = encodeCookie(new String[] {token.getSeries(), token.getTokenValue()});
|
||||||
long maxAge = System.currentTimeMillis() + getTokenValiditySeconds() * 1000;
|
response.addCookie(makeValidCookie(cookieValue, request, getTokenValiditySeconds()));
|
||||||
response.addCookie(makeValidCookie(cookieValue, request, maxAge));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTokenRepository(PersistentTokenRepository tokenRepository) {
|
public void setTokenRepository(PersistentTokenRepository tokenRepository) {
|
||||||
|
@ -4,6 +4,7 @@ import org.springframework.security.AuthenticationException;
|
|||||||
import org.springframework.security.ui.AbstractProcessingFilter;
|
import org.springframework.security.ui.AbstractProcessingFilter;
|
||||||
import org.springframework.security.ui.FilterChainOrderUtils;
|
import org.springframework.security.ui.FilterChainOrderUtils;
|
||||||
import org.springframework.security.ui.SpringSecurityFilter;
|
import org.springframework.security.ui.SpringSecurityFilter;
|
||||||
|
import org.springframework.security.ui.rememberme.AbstractRememberMeServices;
|
||||||
import org.springframework.security.ui.rememberme.TokenBasedRememberMeServices;
|
import org.springframework.security.ui.rememberme.TokenBasedRememberMeServices;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
@ -35,8 +36,8 @@ public class DefaultLoginPageGeneratingFilter extends SpringSecurityFilter {
|
|||||||
usernameParameter = authFilter.getUsernameParameter();
|
usernameParameter = authFilter.getUsernameParameter();
|
||||||
passwordParameter = authFilter.getPasswordParameter();
|
passwordParameter = authFilter.getPasswordParameter();
|
||||||
|
|
||||||
if (authFilter.getRememberMeServices() instanceof TokenBasedRememberMeServices) {
|
if (authFilter.getRememberMeServices() instanceof AbstractRememberMeServices) {
|
||||||
rememberMeParameter = ((TokenBasedRememberMeServices)authFilter.getRememberMeServices()).getParameter();
|
rememberMeParameter = ((AbstractRememberMeServices)authFilter.getRememberMeServices()).getParameter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ protect.attlist &=
|
|||||||
|
|
||||||
http =
|
http =
|
||||||
## Container element for HTTP security configuration
|
## 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 &=
|
http.attlist &=
|
||||||
## Controls the eagerness with which an HTTP session is created.
|
## Controls the eagerness with which an HTTP session is created.
|
||||||
[ a:defaultValue = "ifRequired" ] attribute createSession {"ifRequired" | "always" | "never" }?
|
[ a:defaultValue = "ifRequired" ] attribute createSession {"ifRequired" | "always" | "never" }?
|
||||||
@ -135,6 +135,13 @@ concurrent-sessions.attlist &=
|
|||||||
concurrent-sessions.attlist &=
|
concurrent-sessions.attlist &=
|
||||||
attribute exceptionIfMaximumExceeded {"true" | "false"}?
|
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 =
|
authentication-provider =
|
||||||
element authentication-provider {authentication-provider.attlist, (user-service | jdbc-user-service)}
|
element authentication-provider {authentication-provider.attlist, (user-service | jdbc-user-service)}
|
||||||
authentication-provider.attlist &= empty
|
authentication-provider.attlist &= empty
|
||||||
@ -144,7 +151,6 @@ user-service =
|
|||||||
user-service.attlist &=
|
user-service.attlist &=
|
||||||
attribute properties {xsd:string}*
|
attribute properties {xsd:string}*
|
||||||
|
|
||||||
|
|
||||||
user =
|
user =
|
||||||
element user {user.attlist, empty}
|
element user {user.attlist, empty}
|
||||||
user.attlist &=
|
user.attlist &=
|
||||||
|
@ -72,13 +72,14 @@
|
|||||||
<xs:documentation>Container element for HTTP security configuration</xs:documentation>
|
<xs:documentation>Container element for HTTP security configuration</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
<xs:complexType>
|
<xs:complexType>
|
||||||
<xs:sequence>
|
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
<xs:element maxOccurs="unbounded" ref="security:intercept-url"/>
|
<xs:element ref="security:intercept-url"/>
|
||||||
<xs:element minOccurs="0" ref="security:form-login"/>
|
<xs:element ref="security:form-login"/>
|
||||||
<xs:element minOccurs="0" ref="security:http-basic"/>
|
<xs:element ref="security:http-basic"/>
|
||||||
<xs:element minOccurs="0" ref="security:logout"/>
|
<xs:element ref="security:logout"/>
|
||||||
<xs:element minOccurs="0" ref="security:concurrent-session-control"/>
|
<xs:element ref="security:concurrent-session-control"/>
|
||||||
</xs:sequence>
|
<xs:element ref="security:remember-me"/>
|
||||||
|
</xs:choice>
|
||||||
<xs:attributeGroup ref="security:http.attlist"/>
|
<xs:attributeGroup ref="security:http.attlist"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
@ -241,6 +242,16 @@
|
|||||||
</xs:simpleType>
|
</xs:simpleType>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
</xs:attributeGroup>
|
</xs:attributeGroup>
|
||||||
|
<xs:element name="remember-me">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attributeGroup ref="security:remember-me.attlist"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:attributeGroup name="remember-me.attlist">
|
||||||
|
<xs:attribute name="key" use="required" type="xs:string"/>
|
||||||
|
<xs:attribute name="tokenRepository" type="xs:string"/>
|
||||||
|
<xs:attribute name="datasource" type="xs:string"/>
|
||||||
|
</xs:attributeGroup>
|
||||||
<xs:element name="authentication-provider">
|
<xs:element name="authentication-provider">
|
||||||
<xs:complexType>
|
<xs:complexType>
|
||||||
<xs:choice>
|
<xs:choice>
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
package org.springframework.security.config;
|
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.context.support.ClassPathXmlApplicationContext;
|
||||||
import org.springframework.security.concurrent.ConcurrentSessionFilter;
|
import org.springframework.security.concurrent.ConcurrentSessionFilter;
|
||||||
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
|
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
|
||||||
import org.springframework.security.intercept.web.FilterSecurityInterceptor;
|
import org.springframework.security.intercept.web.FilterSecurityInterceptor;
|
||||||
import org.springframework.security.ui.ExceptionTranslationFilter;
|
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.basicauth.BasicProcessingFilter;
|
||||||
import org.springframework.security.ui.logout.LogoutFilter;
|
import org.springframework.security.ui.logout.LogoutFilter;
|
||||||
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
|
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
|
||||||
import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter;
|
import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter;
|
||||||
import org.springframework.security.util.FilterChainProxy;
|
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.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -54,7 +56,7 @@ public class HttpSecurityBeanDefinitionParserTests {
|
|||||||
|
|
||||||
List filterList = filterChainProxy.getFilters("/someurl");
|
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();
|
Iterator filters = filterList.iterator();
|
||||||
|
|
||||||
@ -64,6 +66,7 @@ public class HttpSecurityBeanDefinitionParserTests {
|
|||||||
assertTrue(filters.next() instanceof AuthenticationProcessingFilter);
|
assertTrue(filters.next() instanceof AuthenticationProcessingFilter);
|
||||||
assertTrue(filters.next() instanceof DefaultLoginPageGeneratingFilter);
|
assertTrue(filters.next() instanceof DefaultLoginPageGeneratingFilter);
|
||||||
assertTrue(filters.next() instanceof BasicProcessingFilter);
|
assertTrue(filters.next() instanceof BasicProcessingFilter);
|
||||||
|
assertTrue(filters.next() instanceof RememberMeProcessingFilter);
|
||||||
assertTrue(filters.next() instanceof ExceptionTranslationFilter);
|
assertTrue(filters.next() instanceof ExceptionTranslationFilter);
|
||||||
assertTrue(filters.next() instanceof FilterSecurityInterceptor);
|
assertTrue(filters.next() instanceof FilterSecurityInterceptor);
|
||||||
}
|
}
|
||||||
|
@ -7,20 +7,22 @@
|
|||||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
|
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
|
||||||
|
|
||||||
<security:http createSession="ifRequired" pathType="ant" lowerCaseComparisons="true">
|
<security:http createSession="ifRequired" pathType="ant" lowerCaseComparisons="true">
|
||||||
<security:intercept-url pattern="/unprotected" filters="none"/>
|
<security:intercept-url pattern="/unprotected" filters="none" />
|
||||||
<security:intercept-url pattern="/somepath" access="ROLE_SPECIAL,ROLE_USER" />
|
<security:intercept-url pattern="/somepath" access="ROLE_SPECIAL,ROLE_USER" />
|
||||||
<security:intercept-url pattern="/**" access="ROLE_USER" />
|
<security:intercept-url pattern="/**" access="ROLE_USER" />
|
||||||
|
|
||||||
<!-- Default form login configuration. Will create filter and entry point -->
|
<!-- Default form login configuration. Will create filter and entry point -->
|
||||||
<security:form-login loginUrl="/j_spring_security_check" />
|
<security:form-login loginUrl="/j_spring_security_check" />
|
||||||
|
|
||||||
<!-- Default basic auth configuration. Will create filter and entry point -->
|
<!-- Default basic auth configuration. Will create filter and entry point -->
|
||||||
<security:http-basic realm="NamespaceTestRealm" />
|
<security:http-basic realm="NamespaceTestRealm" />
|
||||||
|
|
||||||
<!-- Default logout configuration -->
|
<!-- Default logout configuration -->
|
||||||
<security:logout logoutUrl="/j_spring_security_logout" logoutSuccessUrl="/" invalidateSession="true" />
|
<security:logout logoutUrl="/j_spring_security_logout" logoutSuccessUrl="/" invalidateSession="true" />
|
||||||
|
|
||||||
<security:concurrent-session-control maxSessions="1"/>
|
<security:concurrent-session-control maxSessions="1"/>
|
||||||
|
|
||||||
|
<security:remember-me key="doesntmatter" tokenRepository="tokenRepo"/>
|
||||||
</security:http>
|
</security:http>
|
||||||
|
|
||||||
<security:authentication-provider>
|
<security:authentication-provider>
|
||||||
@ -29,4 +31,9 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
|
|||||||
<security:user name="bill" password="billspassword" authorities="ROLE_A,ROLE_B,AUTH_OTHER" />
|
<security:user name="bill" password="billspassword" authorities="ROLE_A,ROLE_B,AUTH_OTHER" />
|
||||||
</security:user-service>
|
</security:user-service>
|
||||||
</security:authentication-provider>
|
</security:authentication-provider>
|
||||||
|
|
||||||
|
<bean name="tokenRepo" class="org.springframework.security.ui.rememberme.InMemoryTokenRepositoryImpl"/>
|
||||||
|
|
||||||
|
<!-- bean name="rememberMeServices" class="org.springframework.security.ui.rememberme.NullRememberMeServices"/ -->
|
||||||
|
|
||||||
</beans>
|
</beans>
|
@ -22,8 +22,10 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
|
|||||||
<security:logout />
|
<security:logout />
|
||||||
<security:concurrent-session-control maxSessions="1" exceptionIfMaximumExceeded="true"/>
|
<security:concurrent-session-control maxSessions="1" exceptionIfMaximumExceeded="true"/>
|
||||||
|
|
||||||
|
<security:remember-me key="doesntmatter" tokenRepository="tokenRepo"/>
|
||||||
</security:http>
|
</security:http>
|
||||||
|
|
||||||
|
<bean name="tokenRepo" class="org.springframework.security.ui.rememberme.InMemoryTokenRepositoryImpl"/>
|
||||||
|
|
||||||
<security:authentication-provider>
|
<security:authentication-provider>
|
||||||
<security:user-service>
|
<security:user-service>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user