SEC-1229: Added session-management element to namespace and refactored existing session-related attributes and concurrency control. Refactored <http> parsing code to split it up into more manageable units.

This commit is contained in:
Luke Taylor 2009-09-29 00:29:09 +00:00
parent b8c0bb71f1
commit aa153681bf
12 changed files with 2574 additions and 2826 deletions

11
config/convert_schema.sh Executable file
View File

@ -0,0 +1,11 @@
#! /bin/sh
pushd src/main/resources/org/springframework/security/config/
echo "Converting rnc file to xsd ..."
java -jar ~/bin/trang.jar spring-security-3.0.rnc spring-security-3.0.xsd
echo "Applying XSL transformation to xsd ..."
xsltproc --output spring-security-3.0.xsd spring-security.xsl spring-security-3.0.xsd
popd

View File

@ -28,7 +28,8 @@ public abstract class Elements {
public static final String PRE_INVOCATION_ADVICE = "pre-invocation-advice";
public static final String POST_INVOCATION_ADVICE = "post-invocation-advice";
public static final String PROTECT = "protect";
public static final String CONCURRENT_SESSIONS = "concurrent-session-control";
public static final String SESSION_MANAGEMENT = "session-management";
public static final String CONCURRENT_SESSIONS = "concurrency-control";
public static final String LOGOUT = "logout";
public static final String FORM_LOGIN = "form-login";
public static final String OPENID_LOGIN = "openid-login";

View File

@ -0,0 +1,614 @@
package org.springframework.security.config.http;
import static org.springframework.security.config.http.FilterChainOrder.*;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
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.authentication.AnonymousAuthenticationProvider;
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
import org.springframework.security.config.Elements;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.AnonymousProcessingFilter;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor;
import org.springframework.security.web.authentication.preauth.x509.X509PreAuthenticatedProcessingFilter;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.authentication.www.BasicProcessingFilter;
import org.springframework.security.web.authentication.www.BasicProcessingFilterEntryPoint;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Handles creation of authentication mechanism filters and related beans for &lt;http&gt; parsing.
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
final class AuthenticationConfigBuilder {
private final Log logger = LogFactory.getLog(getClass());
private static final String ATT_REALM = "realm";
private static final String DEF_REALM = "Spring Security Application";
static final String OPEN_ID_AUTHENTICATION_PROCESSING_FILTER_CLASS = "org.springframework.security.openid.OpenIDAuthenticationProcessingFilter";
static final String OPEN_ID_AUTHENTICATION_PROVIDER_CLASS = "org.springframework.security.openid.OpenIDAuthenticationProvider";
static final String OPEN_ID_CONSUMER_CLASS = "org.springframework.security.openid.OpenID4JavaConsumer";
static final String OPEN_ID_ATTRIBUTE_CLASS = "org.springframework.security.openid.OpenIDAttribute";
static final String AUTHENTICATION_PROCESSING_FILTER_CLASS = "org.springframework.security.web.authentication.UsernamePasswordAuthenticationProcessingFilter";
private static final String ATT_AUTO_CONFIG = "auto-config";
private static final String ATT_ACCESS_DENIED_PAGE = "access-denied-page";
private static final String ATT_ACCESS_DENIED_ERROR_PAGE = "error-page";
private static final String ATT_ENTRY_POINT_REF = "entry-point-ref";
private static final String ATT_USER_SERVICE_REF = "user-service-ref";
private static final String ATT_REF = "ref";
private Element httpElt;
private ParserContext pc;
private final boolean autoConfig;
private final boolean allowSessionCreation;
private final String portMapperName;
private RootBeanDefinition anonymousFilter;
private BeanReference anonymousProviderRef;
private BeanDefinition rememberMeFilter;
private String rememberMeServicesId;
private BeanReference rememberMeProviderRef;
private BeanDefinition basicFilter;
private BeanDefinition basicEntryPoint;
private RootBeanDefinition formFilter;
private BeanDefinition formEntryPoint;
private RootBeanDefinition openIDFilter;
private BeanDefinition openIDEntryPoint;
private BeanReference openIDProviderRef;
private String openIDProviderId;
private String formFilterId = null;
private String openIDFilterId = null;
private BeanDefinition x509Filter;
private BeanDefinition x509EntryPoint;
private BeanReference x509ProviderRef;
private String x509ProviderId;
private BeanDefinition logoutFilter;
private BeanDefinition loginPageGenerationFilter;
private BeanDefinition etf;
private BeanReference requestCache;
final SecureRandom random;
public AuthenticationConfigBuilder(Element element, ParserContext pc, boolean allowSessionCreation,
String portMapperName) {
this.httpElt = element;
this.pc = pc;
this.portMapperName = portMapperName;
autoConfig = "true".equals(element.getAttribute(ATT_AUTO_CONFIG));
this.allowSessionCreation = allowSessionCreation;
try {
random = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
// Shouldn't happen...
throw new RuntimeException("Failed find SHA1PRNG algorithm!");
}
}
void createRememberMeFilter(BeanReference authenticationManager) {
// Parse remember me before logout as RememberMeServices is also a LogoutHandler implementation.
Element rememberMeElt = DomUtils.getChildElementByTagName(httpElt, Elements.REMEMBER_ME);
if (rememberMeElt != null) {
rememberMeFilter = (RootBeanDefinition) new RememberMeBeanDefinitionParser().parse(rememberMeElt, pc);
rememberMeFilter.getPropertyValues().addPropertyValue("authenticationManager", authenticationManager);
rememberMeServicesId = ((RuntimeBeanReference) rememberMeFilter.getPropertyValues().getPropertyValue("rememberMeServices").getValue()).getBeanName();
createRememberMeProvider();
}
}
private void createRememberMeProvider() {
RootBeanDefinition provider = new RootBeanDefinition(RememberMeAuthenticationProvider.class);
provider.setSource(rememberMeFilter.getSource());
// Locate the RememberMeServices bean and read the "key" property from it
PropertyValue key = null;
if (pc.getRegistry().containsBeanDefinition(rememberMeServicesId)) {
BeanDefinition services = pc.getRegistry().getBeanDefinition(rememberMeServicesId);
key = services.getPropertyValues().getPropertyValue("key");
}
if (key == null) {
key = new PropertyValue("key", RememberMeBeanDefinitionParser.DEF_KEY);
}
provider.getPropertyValues().addPropertyValue(key);
String id = pc.getReaderContext().registerWithGeneratedName(provider);
pc.registerBeanComponent(new BeanComponentDefinition(provider, id));
rememberMeProviderRef = new RuntimeBeanReference(id);
}
void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
Element formLoginElt = DomUtils.getChildElementByTagName(httpElt, Elements.FORM_LOGIN);
if (formLoginElt != null || autoConfig) {
FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_security_check",
AUTHENTICATION_PROCESSING_FILTER_CLASS, requestCache, sessionStrategy);
parser.parse(formLoginElt, pc);
formFilter = parser.getFilterBean();
formEntryPoint = parser.getEntryPointBean();
}
if (formFilter != null) {
formFilter.getPropertyValues().addPropertyValue("allowSessionCreation", new Boolean(allowSessionCreation));
formFilter.getPropertyValues().addPropertyValue("authenticationManager", authManager);
// Id is required by login page filter
formFilterId = pc.getReaderContext().registerWithGeneratedName(formFilter);
pc.registerBeanComponent(new BeanComponentDefinition(formFilter, formFilterId));
injectRememberMeServicesRef(formFilter, rememberMeServicesId);
}
}
void createOpenIDLoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
Element openIDLoginElt = DomUtils.getChildElementByTagName(httpElt, Elements.OPENID_LOGIN);
if (openIDLoginElt != null) {
FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_openid_security_check",
OPEN_ID_AUTHENTICATION_PROCESSING_FILTER_CLASS, requestCache, sessionStrategy);
parser.parse(openIDLoginElt, pc);
openIDFilter = parser.getFilterBean();
openIDEntryPoint = parser.getEntryPointBean();
Element attrExElt = DomUtils.getChildElementByTagName(openIDLoginElt, Elements.OPENID_ATTRIBUTE_EXCHANGE);
if (attrExElt != null) {
// Set up the consumer with the required attribute list
BeanDefinitionBuilder consumerBldr = BeanDefinitionBuilder.rootBeanDefinition(OPEN_ID_CONSUMER_CLASS);
ManagedList<BeanDefinition> attributes = new ManagedList<BeanDefinition> ();
for (Element attElt : DomUtils.getChildElementsByTagName(attrExElt, Elements.OPENID_ATTRIBUTE)) {
String name = attElt.getAttribute("name");
String type = attElt.getAttribute("type");
String required = attElt.getAttribute("required");
String count = attElt.getAttribute("count");
BeanDefinitionBuilder attrBldr = BeanDefinitionBuilder.rootBeanDefinition(OPEN_ID_ATTRIBUTE_CLASS);
attrBldr.addConstructorArgValue(name);
attrBldr.addConstructorArgValue(type);
if (StringUtils.hasLength(required)) {
attrBldr.addPropertyValue("required", Boolean.valueOf(required));
}
if (StringUtils.hasLength(count)) {
attrBldr.addPropertyValue("count", Integer.parseInt(count));
}
attributes.add(attrBldr.getBeanDefinition());
}
consumerBldr.addConstructorArgValue(attributes);
openIDFilter.getPropertyValues().addPropertyValue("consumer", consumerBldr.getBeanDefinition());
}
}
if (openIDFilter != null) {
openIDFilter.getPropertyValues().addPropertyValue("allowSessionCreation", new Boolean(allowSessionCreation));
openIDFilter.getPropertyValues().addPropertyValue("authenticationManager", authManager);
// Required by login page filter
openIDFilterId = pc.getReaderContext().registerWithGeneratedName(openIDFilter);
pc.getRegistry().registerBeanDefinition(openIDFilterId, openIDFilter);
pc.registerBeanComponent(new BeanComponentDefinition(openIDFilter, openIDFilterId));
injectRememberMeServicesRef(openIDFilter, rememberMeServicesId);
createOpenIDProvider();
}
}
private void createOpenIDProvider() {
Element openIDLoginElt = DomUtils.getChildElementByTagName(httpElt, Elements.OPENID_LOGIN);
BeanDefinitionBuilder openIDProviderBuilder =
BeanDefinitionBuilder.rootBeanDefinition(OPEN_ID_AUTHENTICATION_PROVIDER_CLASS);
String userService = openIDLoginElt.getAttribute(ATT_USER_SERVICE_REF);
if (StringUtils.hasText(userService)) {
openIDProviderBuilder.addPropertyReference("userDetailsService", userService);
}
BeanDefinition openIDProvider = openIDProviderBuilder.getBeanDefinition();
openIDProviderId = pc.getReaderContext().registerWithGeneratedName(openIDProvider);
openIDProviderRef = new RuntimeBeanReference(openIDProviderId);
}
private void injectRememberMeServicesRef(RootBeanDefinition bean, String rememberMeServicesId) {
if (rememberMeServicesId != null) {
bean.getPropertyValues().addPropertyValue("rememberMeServices", new RuntimeBeanReference(rememberMeServicesId));
}
}
void createBasicFilter(BeanReference authManager) {
Element basicAuthElt = DomUtils.getChildElementByTagName(httpElt, Elements.BASIC_AUTH);
String realm = httpElt.getAttribute(ATT_REALM);
if (!StringUtils.hasText(realm)) {
realm = DEF_REALM;
}
RootBeanDefinition filter = null;
RootBeanDefinition entryPoint = null;
if (basicAuthElt != null || autoConfig) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(BasicProcessingFilter.class);
entryPoint = new RootBeanDefinition(BasicProcessingFilterEntryPoint.class);
entryPoint.setSource(pc.extractSource(httpElt));
entryPoint.getPropertyValues().addPropertyValue("realmName", realm);
String entryPointId = pc.getReaderContext().registerWithGeneratedName(entryPoint);
pc.registerBeanComponent(new BeanComponentDefinition(entryPoint, entryPointId));
filterBuilder.addPropertyValue("authenticationManager", authManager);
filterBuilder.addPropertyValue("authenticationEntryPoint", new RuntimeBeanReference(entryPointId));
filter = (RootBeanDefinition) filterBuilder.getBeanDefinition();
}
basicFilter = filter;
basicEntryPoint = entryPoint;
}
void createX509Filter(BeanReference authManager) {
Element x509Elt = DomUtils.getChildElementByTagName(httpElt, Elements.X509);
RootBeanDefinition filter = null;
RootBeanDefinition entryPoint = null;
if (x509Elt != null) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(X509PreAuthenticatedProcessingFilter.class);
filterBuilder.getRawBeanDefinition().setSource(pc.extractSource(x509Elt));
filterBuilder.addPropertyValue("authenticationManager", authManager);
String regex = x509Elt.getAttribute("subject-principal-regex");
if (StringUtils.hasText(regex)) {
BeanDefinitionBuilder extractor = BeanDefinitionBuilder.rootBeanDefinition(SubjectDnX509PrincipalExtractor.class);
extractor.addPropertyValue("subjectDnRegex", regex);
filterBuilder.addPropertyValue("principalExtractor", extractor.getBeanDefinition());
}
filter = (RootBeanDefinition) filterBuilder.getBeanDefinition();
entryPoint = new RootBeanDefinition(Http403ForbiddenEntryPoint.class);
entryPoint.setSource(pc.extractSource(x509Elt));
createX509Provider();
}
x509Filter = filter;
x509EntryPoint = entryPoint;
}
private void createX509Provider() {
Element x509Elt = DomUtils.getChildElementByTagName(httpElt, Elements.X509);
BeanDefinition provider = new RootBeanDefinition(PreAuthenticatedAuthenticationProvider.class);
String userServiceRef = x509Elt.getAttribute(ATT_USER_SERVICE_REF);
if (StringUtils.hasText(userServiceRef)) {
RootBeanDefinition preAuthUserService = new RootBeanDefinition(UserDetailsByNameServiceWrapper.class);
preAuthUserService.setSource(pc.extractSource(x509Elt));
preAuthUserService.getPropertyValues().addPropertyValue("userDetailsService", new RuntimeBeanReference(userServiceRef));
provider.getPropertyValues().addPropertyValue("preAuthenticatedUserDetailsService", preAuthUserService);
}
x509ProviderId = pc.getReaderContext().registerWithGeneratedName(provider);
x509ProviderRef = new RuntimeBeanReference(x509ProviderId);
}
void createLoginPageFilterIfNeeded() {
boolean needLoginPage = formFilter != null || openIDFilter != null;
String formLoginPage = getLoginFormUrl(formEntryPoint);
// If the login URL is the default one, then it is assumed not to have been set explicitly
if (DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL == formLoginPage) {
formLoginPage = null;
}
String openIDLoginPage = getLoginFormUrl(openIDEntryPoint);
// If no login page has been defined, add in the default page generator.
if (needLoginPage && formLoginPage == null && openIDLoginPage == null) {
logger.info("No login page configured. The default internal one will be used. Use the '"
+ FormLoginBeanDefinitionParser.ATT_LOGIN_PAGE + "' attribute to set the URL of the login page.");
BeanDefinitionBuilder loginPageFilter =
BeanDefinitionBuilder.rootBeanDefinition(DefaultLoginPageGeneratingFilter.class);
if (formFilter != null) {
loginPageFilter.addConstructorArgReference(formFilterId);
}
if (openIDFilter != null) {
loginPageFilter.addConstructorArgReference(openIDFilterId);
}
loginPageGenerationFilter = loginPageFilter.getBeanDefinition();
}
}
void createLogoutFilter() {
Element logoutElt = DomUtils.getChildElementByTagName(httpElt, Elements.LOGOUT);
if (logoutElt != null || autoConfig) {
logoutFilter = new LogoutBeanDefinitionParser(rememberMeServicesId).parse(logoutElt, pc);
}
}
void createAnonymousFilter() {
Element anonymousElt = DomUtils.getChildElementByTagName(httpElt, Elements.ANONYMOUS);
if (anonymousElt != null && "false".equals(anonymousElt.getAttribute("enabled"))) {
return;
}
String grantedAuthority = null;
String username = null;
String key = null;
Object source = pc.extractSource(httpElt);
if (anonymousElt != null) {
grantedAuthority = httpElt.getAttribute("granted-authority");
username = httpElt.getAttribute("username");
key = httpElt.getAttribute("key");
source = pc.extractSource(anonymousElt);
}
if (!StringUtils.hasText(grantedAuthority)) {
grantedAuthority = "ROLE_ANONYMOUS";
}
if (!StringUtils.hasText(username)) {
username = "anonymousUser";
}
if (!StringUtils.hasText(key)) {
// Generate a random key for the Anonymous provider
key = Long.toString(random.nextLong());
}
anonymousFilter = new RootBeanDefinition(AnonymousProcessingFilter.class);
PropertyValue keyPV = new PropertyValue("key", key);
anonymousFilter.setSource(source);
anonymousFilter.getPropertyValues().addPropertyValue("userAttribute", username + "," + grantedAuthority);
anonymousFilter.getPropertyValues().addPropertyValue(keyPV);
RootBeanDefinition anonymousProviderBean = new RootBeanDefinition(AnonymousAuthenticationProvider.class);
anonymousProviderBean.setSource(anonymousFilter.getSource());
anonymousProviderBean.getPropertyValues().addPropertyValue(keyPV);
String id = pc.getReaderContext().registerWithGeneratedName(anonymousProviderBean);
pc.registerBeanComponent(new BeanComponentDefinition(anonymousProviderBean, id));
anonymousProviderRef = new RuntimeBeanReference(id);
}
void createExceptionTranslationFilter() {
BeanDefinitionBuilder etfBuilder = BeanDefinitionBuilder.rootBeanDefinition(ExceptionTranslationFilter.class);
etfBuilder.addPropertyValue("accessDeniedHandler", createAccessDeniedHandler(httpElt, pc));
assert requestCache != null;
etfBuilder.addPropertyValue("requestCache", requestCache);
etfBuilder.addPropertyValue("authenticationEntryPoint", selectEntryPoint());
etf = etfBuilder.getBeanDefinition();
}
void createRequestCache() {
Element requestCacheElt = DomUtils.getChildElementByTagName(httpElt, Elements.REQUEST_CACHE);
if (requestCacheElt != null) {
requestCache = new RuntimeBeanReference(requestCacheElt.getAttribute(ATT_REF));
return;
}
BeanDefinitionBuilder requestCacheBldr = BeanDefinitionBuilder.rootBeanDefinition(HttpSessionRequestCache.class);
BeanDefinitionBuilder portResolver = BeanDefinitionBuilder.rootBeanDefinition(PortResolverImpl.class);
portResolver.addPropertyReference("portMapper", portMapperName);
requestCacheBldr.addPropertyValue("createSessionAllowed", allowSessionCreation);
requestCacheBldr.addPropertyValue("portResolver", portResolver.getBeanDefinition());
BeanDefinition bean = requestCacheBldr.getBeanDefinition();
String id = pc.getReaderContext().registerWithGeneratedName(bean);
pc.registerBeanComponent(new BeanComponentDefinition(bean, id));
this.requestCache = new RuntimeBeanReference(id);
}
private BeanMetadataElement createAccessDeniedHandler(Element element, ParserContext pc) {
String accessDeniedPage = element.getAttribute(ATT_ACCESS_DENIED_PAGE);
WebConfigUtils.validateHttpRedirect(accessDeniedPage, pc, pc.extractSource(element));
Element accessDeniedElt = DomUtils.getChildElementByTagName(element, Elements.ACCESS_DENIED_HANDLER);
BeanDefinitionBuilder accessDeniedHandler = BeanDefinitionBuilder.rootBeanDefinition(AccessDeniedHandlerImpl.class);
if (StringUtils.hasText(accessDeniedPage)) {
if (accessDeniedElt != null) {
pc.getReaderContext().error("The attribute " + ATT_ACCESS_DENIED_PAGE +
" cannot be used with <" + Elements.ACCESS_DENIED_HANDLER + ">", pc.extractSource(accessDeniedElt));
}
accessDeniedHandler.addPropertyValue("errorPage", accessDeniedPage);
}
if (accessDeniedElt != null) {
String errorPage = accessDeniedElt.getAttribute("error-page");
String ref = accessDeniedElt.getAttribute("ref");
if (StringUtils.hasText(errorPage)) {
if (StringUtils.hasText(ref)) {
pc.getReaderContext().error("The attribute " + ATT_ACCESS_DENIED_ERROR_PAGE +
" cannot be used together with the 'ref' attribute within <" +
Elements.ACCESS_DENIED_HANDLER + ">", pc.extractSource(accessDeniedElt));
}
accessDeniedHandler.addPropertyValue("errorPage", errorPage);
} else if (StringUtils.hasText(ref)) {
return new RuntimeBeanReference(ref);
}
}
return accessDeniedHandler.getBeanDefinition();
}
private BeanMetadataElement selectEntryPoint() {
// We need to establish the main entry point.
// First check if a custom entry point bean is set
String customEntryPoint = httpElt.getAttribute(ATT_ENTRY_POINT_REF);
if (StringUtils.hasText(customEntryPoint)) {
return new RuntimeBeanReference(customEntryPoint);
}
Element basicAuthElt = DomUtils.getChildElementByTagName(httpElt, Elements.BASIC_AUTH);
Element formLoginElt = DomUtils.getChildElementByTagName(httpElt, Elements.FORM_LOGIN);
Element openIDLoginElt = DomUtils.getChildElementByTagName(httpElt, Elements.OPENID_LOGIN);
// Basic takes precedence if explicit element is used and no others are configured
if (basicAuthElt != null && formLoginElt == null && openIDLoginElt == null) {
return basicEntryPoint;
}
// If formLogin has been enabled either through an element or auto-config, then it is used if no openID login page
// has been set
String openIDLoginPage = getLoginFormUrl(openIDEntryPoint);
if (formFilter != null && openIDLoginPage == null) {
return formEntryPoint;
}
// Otherwise use OpenID if enabled
if (openIDFilter != null && formFilter == null) {
return openIDEntryPoint;
}
// If X.509 has been enabled, use the preauth entry point.
if (DomUtils.getChildElementByTagName(httpElt, Elements.X509) != null) {
return x509EntryPoint;
}
pc.getReaderContext().error("No AuthenticationEntryPoint could be established. Please " +
"make sure you have a login mechanism configured through the namespace (such as form-login) or " +
"specify a custom AuthenticationEntryPoint with the '" + ATT_ENTRY_POINT_REF+ "' attribute ",
pc.extractSource(httpElt));
return null;
}
private String getLoginFormUrl(BeanDefinition entryPoint) {
if (entryPoint == null) {
return null;
}
PropertyValues pvs = entryPoint.getPropertyValues();
PropertyValue pv = pvs.getPropertyValue("loginFormUrl");
if (pv == null) {
return null;
}
return (String) pv.getValue();
}
void createUserServiceInjector() {
BeanDefinitionBuilder userServiceInjector = BeanDefinitionBuilder.rootBeanDefinition(UserDetailsServiceInjectionBeanPostProcessor.class);
userServiceInjector.addConstructorArgValue(x509ProviderId);
userServiceInjector.addConstructorArgValue(rememberMeServicesId);
userServiceInjector.addConstructorArgValue(openIDProviderId);
userServiceInjector.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
pc.getReaderContext().registerWithGeneratedName(userServiceInjector.getBeanDefinition());
}
List<OrderDecorator> getFilters() {
List<OrderDecorator> filters = new ArrayList<OrderDecorator>();
if (anonymousFilter != null) {
filters.add(new OrderDecorator(anonymousFilter, ANONYMOUS_FILTER));
}
if (rememberMeFilter != null) {
filters.add(new OrderDecorator(rememberMeFilter, REMEMBER_ME_FILTER));
}
if (logoutFilter != null) {
filters.add(new OrderDecorator(logoutFilter, LOGOUT_FILTER));
}
if (x509Filter != null) {
filters.add(new OrderDecorator(x509Filter, X509_FILTER));
}
if (formFilter != null) {
filters.add(new OrderDecorator(formFilter, AUTHENTICATION_PROCESSING_FILTER));
}
if (openIDFilter != null) {
filters.add(new OrderDecorator(openIDFilter, OPENID_PROCESSING_FILTER));
}
if (loginPageGenerationFilter != null) {
filters.add(new OrderDecorator(loginPageGenerationFilter, LOGIN_PAGE_FILTER));
}
if (basicFilter != null) {
filters.add(new OrderDecorator(basicFilter, BASIC_PROCESSING_FILTER));
}
filters.add(new OrderDecorator(etf, EXCEPTION_TRANSLATION_FILTER));
return filters;
}
List<BeanReference> getProviders() {
List<BeanReference> providers = new ArrayList<BeanReference>();
if (anonymousProviderRef != null) {
providers.add(anonymousProviderRef);
}
if (rememberMeProviderRef != null) {
providers.add(rememberMeProviderRef);
}
if (openIDProviderRef != null) {
providers.add(openIDProviderRef);
}
if (x509ProviderRef != null) {
providers.add(x509ProviderRef);
}
return providers;
}
public BeanReference getRequestCache() {
return requestCache;
}
}

View File

@ -0,0 +1,520 @@
package org.springframework.security.config.http;
import static org.springframework.security.config.http.FilterChainOrder.*;
import static org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
import org.springframework.security.config.Elements;
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.access.channel.InsecureChannelProcessor;
import org.springframework.security.web.access.channel.RetryWithHttpEntryPoint;
import org.springframework.security.web.access.channel.RetryWithHttpsEntryPoint;
import org.springframework.security.web.access.channel.SecureChannelProcessor;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.access.intercept.RequestKey;
import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.session.ConcurrentSessionControlAuthenticatedSessionStrategy;
import org.springframework.security.web.session.DefaultSessionAuthenticationStrategy;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;
import org.springframework.security.web.wrapper.SecurityContextHolderAwareRequestFilter;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Stateful class which helps HttpSecurityBDP to create the configuration for the &lt;http&gt; element.
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
class HttpConfigurationBuilder {
private final Log logger = LogFactory.getLog(getClass());
private static final String ATT_CREATE_SESSION = "create-session";
private static final String OPT_CREATE_SESSION_NEVER = "never";
private static final String DEF_CREATE_SESSION_IF_REQUIRED = "ifRequired";
private static final String OPT_CREATE_SESSION_ALWAYS = "always";
private static final String ATT_SESSION_FIXATION_PROTECTION = "session-fixation-protection";
private static final String OPT_SESSION_FIXATION_NO_PROTECTION = "none";
private static final String OPT_SESSION_FIXATION_MIGRATE_SESSION = "migrateSession";
private static final String ATT_INVALID_SESSION_URL = "invalid-session-url";
private static final String ATT_SECURITY_CONTEXT_REPOSITORY = "security-context-repository-ref";
private static final String ATT_DISABLE_URL_REWRITING = "disable-url-rewriting";
private static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
private static final String ATT_USE_EXPRESSIONS = "use-expressions";
private static final String ATT_ONCE_PER_REQUEST = "once-per-request";
private final Element httpElt;
private final ParserContext pc;
private final UrlMatcher matcher;
private final Boolean convertPathsToLowerCase;
private final boolean allowSessionCreation;
private final List<Element> interceptUrls;
// Use ManagedMap to allow placeholder resolution
private List<String> emptyFilterChainPaths;
private ManagedMap<String, List<BeanMetadataElement>> filterChainMap;
private BeanDefinition cpf;
private BeanDefinition securityContextPersistenceFilter;
private BeanReference contextRepoRef;
private BeanReference sessionRegistryRef;
private BeanDefinition concurrentSessionFilter;
private BeanReference sessionStrategyRef;
private RootBeanDefinition sfpf;
private BeanDefinition servApiFilter;
private String portMapperName;
private BeanReference fsi;
public HttpConfigurationBuilder(Element element, ParserContext pc, UrlMatcher matcher, String portMapperName) {
this.httpElt = element;
this.pc = pc;
this.portMapperName = portMapperName;
this.matcher = matcher;
// SEC-501 - should paths stored in request maps be converted to lower case
// true if Ant path and using lower case
convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
interceptUrls = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);
allowSessionCreation = !OPT_CREATE_SESSION_NEVER.equals(element.getAttribute(ATT_CREATE_SESSION));
}
void parseInterceptUrlsForEmptyFilterChains() {
emptyFilterChainPaths = new ArrayList<String>();
filterChainMap = new ManagedMap<String, List<BeanMetadataElement>>();
for (Element urlElt : interceptUrls) {
String path = urlElt.getAttribute(ATT_PATH_PATTERN);
if(!StringUtils.hasText(path)) {
pc.getReaderContext().error("path attribute cannot be empty or null", urlElt);
}
if (convertPathsToLowerCase) {
path = path.toLowerCase();
}
String filters = urlElt.getAttribute(ATT_FILTERS);
if (StringUtils.hasText(filters)) {
if (!filters.equals(OPT_FILTERS_NONE)) {
pc.getReaderContext().error("Currently only 'none' is supported as the custom " +
"filters attribute", urlElt);
}
emptyFilterChainPaths.add(path);
List<BeanMetadataElement> noFilters = Collections.emptyList();
filterChainMap.put(path, noFilters);
}
}
}
void createSecurityContextPersistenceFilter() {
BeanDefinitionBuilder scpf = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextPersistenceFilter.class);
String repoRef = httpElt.getAttribute(ATT_SECURITY_CONTEXT_REPOSITORY);
String createSession = httpElt.getAttribute(ATT_CREATE_SESSION);
String disableUrlRewriting = httpElt.getAttribute(ATT_DISABLE_URL_REWRITING);
if (StringUtils.hasText(repoRef)) {
if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) {
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
} else if (StringUtils.hasText(createSession)) {
pc.getReaderContext().error("If using security-context-repository-ref, the only value you can set for " +
"'create-session' is 'always'. Other session creation logic should be handled by the " +
"SecurityContextRepository", httpElt);
}
} else {
BeanDefinitionBuilder contextRepo = BeanDefinitionBuilder.rootBeanDefinition(HttpSessionSecurityContextRepository.class);
if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) {
contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE);
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
} else if (OPT_CREATE_SESSION_NEVER.equals(createSession)) {
contextRepo.addPropertyValue("allowSessionCreation", Boolean.FALSE);
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
} else {
createSession = DEF_CREATE_SESSION_IF_REQUIRED;
contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE);
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
}
if ("true".equals(disableUrlRewriting)) {
contextRepo.addPropertyValue("disableUrlRewriting", Boolean.TRUE);
}
BeanDefinition repoBean = contextRepo.getBeanDefinition();
repoRef = pc.getReaderContext().registerWithGeneratedName(repoBean);
pc.registerBeanComponent(new BeanComponentDefinition(repoBean, repoRef));
}
contextRepoRef = new RuntimeBeanReference(repoRef);
scpf.addPropertyValue("securityContextRepository", contextRepoRef);
securityContextPersistenceFilter = scpf.getBeanDefinition();
}
void createSessionManagementFilters() {
Element sessionMgmtElt = DomUtils.getChildElementByTagName(httpElt, Elements.SESSION_MANAGEMENT);
Element sessionCtrlEt = null;
String sessionFixationAttribute = null;
String invalidSessionUrl = null;
if (sessionMgmtElt != null) {
sessionFixationAttribute = sessionMgmtElt.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
invalidSessionUrl = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_URL);
sessionCtrlEt = DomUtils.getChildElementByTagName(sessionMgmtElt, Elements.CONCURRENT_SESSIONS);
if (sessionCtrlEt != null) {
createConcurrencyControlFilterAndSessionRegistry(sessionCtrlEt);
}
}
if (!StringUtils.hasText(sessionFixationAttribute)) {
sessionFixationAttribute = OPT_SESSION_FIXATION_MIGRATE_SESSION;
}
boolean sessionFixationProtectionRequired = !sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION);
BeanDefinitionBuilder sessionStrategy;
if (sessionCtrlEt != null) {
assert sessionRegistryRef != null;
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControlAuthenticatedSessionStrategy.class);
sessionStrategy.addConstructorArgValue(sessionRegistryRef);
String maxSessions = sessionCtrlEt.getAttribute("max-sessions");
if (StringUtils.hasText(maxSessions)) {
sessionStrategy.addPropertyValue("maximumSessions", maxSessions);
}
String exceptionIfMaximumExceeded = sessionCtrlEt.getAttribute("exception-if-maximum-exceeded");
if (StringUtils.hasText(exceptionIfMaximumExceeded)) {
sessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
}
} else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)) {
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultSessionAuthenticationStrategy.class);
} else {
sfpf = null;
return;
}
BeanDefinitionBuilder sessionMgmtFilter = BeanDefinitionBuilder.rootBeanDefinition(SessionManagementFilter.class);
sessionMgmtFilter.addConstructorArgValue(contextRepoRef);
BeanDefinition strategyBean = sessionStrategy.getBeanDefinition();
String sessionStrategyId = pc.getReaderContext().registerWithGeneratedName(strategyBean);
pc.registerBeanComponent(new BeanComponentDefinition(strategyBean, sessionStrategyId));
sessionMgmtFilter.addPropertyReference("authenticatedSessionStrategy", sessionStrategyId);
if (sessionFixationProtectionRequired) {
sessionStrategy.addPropertyValue("migrateSessionAttributes",
Boolean.valueOf(sessionFixationAttribute.equals(OPT_SESSION_FIXATION_MIGRATE_SESSION)));
}
if (StringUtils.hasText(invalidSessionUrl)) {
sessionMgmtFilter.addPropertyValue("invalidSessionUrl", invalidSessionUrl);
}
sfpf = (RootBeanDefinition) sessionMgmtFilter.getBeanDefinition();
sessionStrategyRef = new RuntimeBeanReference(sessionStrategyId);
}
private void createConcurrencyControlFilterAndSessionRegistry(Element element) {
final String ATT_EXPIRY_URL = "expired-url";
final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias";
final String ATT_SESSION_REGISTRY_REF = "session-registry-ref";
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
pc.pushContainingComponent(compositeDef);
BeanDefinitionRegistry beanRegistry = pc.getRegistry();
String sessionRegistryId = element.getAttribute(ATT_SESSION_REGISTRY_REF);
if (!StringUtils.hasText(sessionRegistryId)) {
// Register an internal SessionRegistryImpl if no external reference supplied.
RootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class);
sessionRegistryId = pc.getReaderContext().registerWithGeneratedName(sessionRegistry);
pc.registerComponent(new BeanComponentDefinition(sessionRegistry, sessionRegistryId));
}
String registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS);
if (StringUtils.hasText(registryAlias)) {
beanRegistry.registerAlias(sessionRegistryId, registryAlias);
}
BeanDefinitionBuilder filterBuilder =
BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionFilter.class);
filterBuilder.addPropertyReference("sessionRegistry", sessionRegistryId);
Object source = pc.extractSource(element);
filterBuilder.getRawBeanDefinition().setSource(source);
filterBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String expiryUrl = element.getAttribute(ATT_EXPIRY_URL);
if (StringUtils.hasText(expiryUrl)) {
WebConfigUtils.validateHttpRedirect(expiryUrl, pc, source);
filterBuilder.addPropertyValue("expiredUrl", expiryUrl);
}
pc.popAndRegisterContainingComponent();
concurrentSessionFilter = filterBuilder.getBeanDefinition();
sessionRegistryRef = new RuntimeBeanReference(sessionRegistryId);
}
// Adds the servlet-api integration filter if required
void createServletApiFilter() {
final String ATT_SERVLET_API_PROVISION = "servlet-api-provision";
final String DEF_SERVLET_API_PROVISION = "true";
String provideServletApi = httpElt.getAttribute(ATT_SERVLET_API_PROVISION);
if (!StringUtils.hasText(provideServletApi)) {
provideServletApi = DEF_SERVLET_API_PROVISION;
}
if ("true".equals(provideServletApi)) {
servApiFilter = new RootBeanDefinition(SecurityContextHolderAwareRequestFilter.class);
}
}
void createChannelProcessingFilter() {
ManagedMap<BeanDefinition,BeanDefinition> channelRequestMap = parseInterceptUrlsForChannelSecurity();
if (channelRequestMap.isEmpty()) {
return;
}
RootBeanDefinition channelFilter = new RootBeanDefinition(ChannelProcessingFilter.class);
BeanDefinitionBuilder metadataSourceBldr = BeanDefinitionBuilder.rootBeanDefinition(DefaultFilterInvocationSecurityMetadataSource.class);
metadataSourceBldr.addConstructorArgValue(matcher);
metadataSourceBldr.addConstructorArgValue(channelRequestMap);
metadataSourceBldr.addPropertyValue("stripQueryStringFromUrls", matcher instanceof AntUrlPathMatcher);
channelFilter.getPropertyValues().addPropertyValue("securityMetadataSource", metadataSourceBldr.getBeanDefinition());
RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);
ManagedList<RootBeanDefinition> channelProcessors = new ManagedList<RootBeanDefinition>(3);
RootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class);
RootBeanDefinition retryWithHttp = new RootBeanDefinition(RetryWithHttpEntryPoint.class);
RootBeanDefinition retryWithHttps = new RootBeanDefinition(RetryWithHttpsEntryPoint.class);
RuntimeBeanReference portMapper = new RuntimeBeanReference(portMapperName);
retryWithHttp.getPropertyValues().addPropertyValue("portMapper", portMapper);
retryWithHttps.getPropertyValues().addPropertyValue("portMapper", portMapper);
secureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttps);
RootBeanDefinition inSecureChannelProcessor = new RootBeanDefinition(InsecureChannelProcessor.class);
inSecureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttp);
channelProcessors.add(secureChannelProcessor);
channelProcessors.add(inSecureChannelProcessor);
channelDecisionManager.getPropertyValues().addPropertyValue("channelProcessors", channelProcessors);
String id = pc.getReaderContext().registerWithGeneratedName(channelDecisionManager);
channelFilter.getPropertyValues().addPropertyValue("channelDecisionManager", new RuntimeBeanReference(id));
cpf = channelFilter;
}
/**
* Parses the intercept-url elements to obtain the map used by channel security.
* This will be empty unless the <tt>requires-channel</tt> attribute has been used on a URL path.
*/
private ManagedMap<BeanDefinition,BeanDefinition> parseInterceptUrlsForChannelSecurity() {
ManagedMap<BeanDefinition, BeanDefinition> channelRequestMap = new ManagedMap<BeanDefinition, BeanDefinition>();
for (Element urlElt : interceptUrls) {
String path = urlElt.getAttribute(ATT_PATH_PATTERN);
if(!StringUtils.hasText(path)) {
pc.getReaderContext().error("path attribute cannot be empty or null", urlElt);
}
if (convertPathsToLowerCase) {
path = path.toLowerCase();
}
String requiredChannel = urlElt.getAttribute(ATT_REQUIRES_CHANNEL);
if (StringUtils.hasText(requiredChannel)) {
BeanDefinition requestKey = new RootBeanDefinition(RequestKey.class);
requestKey.getConstructorArgumentValues().addGenericArgumentValue(path);
RootBeanDefinition channelAttributes = new RootBeanDefinition(ChannelAttributeFactory.class);
channelAttributes.getConstructorArgumentValues().addGenericArgumentValue(requiredChannel);
channelAttributes.setFactoryMethodName("createChannelAttributes");
channelRequestMap.put(requestKey, channelAttributes);
}
}
return channelRequestMap;
}
void createFilterSecurityInterceptor(BeanReference authManager) {
BeanDefinitionBuilder fidsBuilder;
boolean useExpressions = "true".equals(httpElt.getAttribute(ATT_USE_EXPRESSIONS));
ManagedMap<BeanDefinition,BeanDefinition> requestToAttributesMap =
parseInterceptUrlsForFilterInvocationRequestMap(DomUtils.getChildElementsByTagName(httpElt, Elements.INTERCEPT_URL),
convertPathsToLowerCase, useExpressions, pc);
RootBeanDefinition accessDecisionMgr;
ManagedList<BeanDefinition> voters = new ManagedList<BeanDefinition>(2);
if (useExpressions) {
Element expressionHandlerElt = DomUtils.getChildElementByTagName(httpElt, Elements.EXPRESSION_HANDLER);
String expressionHandlerRef = expressionHandlerElt == null ? null : expressionHandlerElt.getAttribute("ref");
if (StringUtils.hasText(expressionHandlerRef)) {
logger.info("Using bean '" + expressionHandlerRef + "' as web SecurityExpressionHandler implementation");
} else {
BeanDefinition expressionHandler = BeanDefinitionBuilder.rootBeanDefinition(EXPRESSION_HANDLER_CLASS).getBeanDefinition();
expressionHandlerRef = pc.getReaderContext().registerWithGeneratedName(expressionHandler);
pc.registerBeanComponent(new BeanComponentDefinition(expressionHandler, expressionHandlerRef));
}
fidsBuilder = BeanDefinitionBuilder.rootBeanDefinition(EXPRESSION_FIMDS_CLASS);
fidsBuilder.addConstructorArgValue(matcher);
fidsBuilder.addConstructorArgValue(requestToAttributesMap);
fidsBuilder.addConstructorArgReference(expressionHandlerRef);
voters.add(new RootBeanDefinition(WebExpressionVoter.class));
} else {
fidsBuilder = BeanDefinitionBuilder.rootBeanDefinition(DefaultFilterInvocationSecurityMetadataSource.class);
fidsBuilder.addConstructorArgValue(matcher);
fidsBuilder.addConstructorArgValue(requestToAttributesMap);
voters.add(new RootBeanDefinition(RoleVoter.class));
voters.add(new RootBeanDefinition(AuthenticatedVoter.class));
}
accessDecisionMgr = new RootBeanDefinition(AffirmativeBased.class);
accessDecisionMgr.getPropertyValues().addPropertyValue("decisionVoters", voters);
accessDecisionMgr.setSource(pc.extractSource(httpElt));
fidsBuilder.addPropertyValue("stripQueryStringFromUrls", matcher instanceof AntUrlPathMatcher);
// Set up the access manager reference for http
String accessManagerId = httpElt.getAttribute(ATT_ACCESS_MGR);
if (!StringUtils.hasText(accessManagerId)) {
accessManagerId = pc.getReaderContext().registerWithGeneratedName(accessDecisionMgr);
pc.registerBeanComponent(new BeanComponentDefinition(accessDecisionMgr, accessManagerId));
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);
builder.addPropertyReference("accessDecisionManager", accessManagerId);
builder.addPropertyValue("authenticationManager", authManager);
if ("false".equals(httpElt.getAttribute(ATT_ONCE_PER_REQUEST))) {
builder.addPropertyValue("observeOncePerRequest", Boolean.FALSE);
}
builder.addPropertyValue("securityMetadataSource", fidsBuilder.getBeanDefinition());
BeanDefinition fsiBean = builder.getBeanDefinition();
String fsiId = pc.getReaderContext().registerWithGeneratedName(fsiBean);
pc.registerBeanComponent(new BeanComponentDefinition(fsiBean,fsiId));
// Create and register a DefaultWebInvocationPrivilegeEvaluator for use with taglibs etc.
BeanDefinition wipe = new RootBeanDefinition(DefaultWebInvocationPrivilegeEvaluator.class);
wipe.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(fsiId));
String wipeId = pc.getReaderContext().registerWithGeneratedName(wipe);
pc.registerBeanComponent(new BeanComponentDefinition(wipe, wipeId));
this.fsi = new RuntimeBeanReference(fsiId);
}
/**
* Parses the filter invocation map which will be used to configure the FilterInvocationSecurityMetadataSource
* used in the security interceptor.
*/
private static ManagedMap<BeanDefinition,BeanDefinition>
parseInterceptUrlsForFilterInvocationRequestMap(List<Element> urlElts, boolean useLowerCasePaths,
boolean useExpressions, ParserContext parserContext) {
return FilterInvocationSecurityMetadataSourceBeanDefinitionParser.parseInterceptUrlsForFilterInvocationRequestMap(urlElts, useLowerCasePaths, useExpressions, parserContext);
}
BeanReference getSessionStrategy() {
return sessionStrategyRef;
}
boolean isAllowSessionCreation() {
return allowSessionCreation;
}
List<String> getEmptyFilterChainPaths() {
return emptyFilterChainPaths;
}
List<OrderDecorator> getFilters() {
List<OrderDecorator> filters = new ArrayList<OrderDecorator>();
if (cpf != null) {
filters.add(new OrderDecorator(cpf, CHANNEL_FILTER));
}
if (concurrentSessionFilter != null) {
filters.add(new OrderDecorator(concurrentSessionFilter, CONCURRENT_SESSION_FILTER));
}
filters.add(new OrderDecorator(securityContextPersistenceFilter, SECURITY_CONTEXT_FILTER));
if (servApiFilter != null) {
filters.add(new OrderDecorator(servApiFilter, SERVLET_API_SUPPORT_FILTER));
}
if (sfpf != null) {
filters.add(new OrderDecorator(sfpf, SESSION_FIXATION_FILTER));
}
filters.add(new OrderDecorator(fsi, FILTER_SECURITY_INTERCEPTOR));
return filters;
}
}

View File

@ -130,7 +130,7 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP
BeanDefinition expressionHandler = new RootBeanDefinition(DefaultMethodSecurityExpressionHandler.class);
expressionHandlerRef = pc.getReaderContext().registerWithGeneratedName(expressionHandler);
pc.registerBeanComponent(new BeanComponentDefinition(expressionHandler, expressionHandlerRef));
logger.warn("Expressions were enabled for method security but no SecurityExpressionHandler was configured. " +
logger.info("Expressions were enabled for method security but no SecurityExpressionHandler was configured. " +
"All hasPermision() expressions will evaluate to false.");
}

View File

@ -247,7 +247,7 @@ protect-pointcut.attlist &=
http =
## Container element for HTTP security configuration
element http {http.attlist, (intercept-url+ & access-denied-handler? & form-login? & openid-login? & x509? & http-basic? & logout? & concurrent-session-control? & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache?) }
element http {http.attlist, (intercept-url+ & access-denied-handler? & form-login? & openid-login? & x509? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache?) }
http.attlist &=
## Automatically registers a login form, BASIC authentication, anonymous authentication, logout services, remember-me and servlet-api-integration. If set to "true", all of these capabilities are added (although you can still customize the configuration of each by providing the respective element). If unspecified, defaults to "false".
attribute auto-config {boolean}?
@ -274,9 +274,6 @@ http.attlist &=
http.attlist &=
## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to "Spring Security Application".
attribute realm {xsd:token}?
http.attlist &=
## Indicates whether an existing session should be invalidated when a user authenticates and a new session started. If set to "none" no change will be made. "newSession" will create a new empty session. "migrateSession" will create a new session and copy the session attributes to the new session. Defaults to "migrateSession".
attribute session-fixation-protection {"none" | "newSession" | "migrateSession" }?
http.attlist &=
## Allows a customized AuthenticationEntryPoint to be used.
attribute entry-point-ref {xsd:token}?
@ -289,9 +286,7 @@ http.attlist &=
http.attlist &=
##
attribute disable-url-rewriting {boolean}?
http.attlist &=
## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.
attribute invalid-session-url {xsd:token}?
access-denied-handler =
## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.
@ -421,28 +416,37 @@ http-basic =
## Adds support for basic authentication (this is an element to permit future expansion, such as supporting an "ignoreFailure" attribute)
element http-basic {empty}
session-management =
element session-management {session-management.attlist, concurrency-control?}
concurrent-session-control =
## Adds support for concurrent session control, allowing limits to be placed on the number of sessions a user can have.
element concurrent-session-control {concurrent-sessions.attlist, empty}
concurrent-sessions.attlist &=
## The maximum number of sessions a single user can have open at the same time. Defaults to "1".
session-management.attlist &=
## Indicates whether an existing session should be invalidated when a user authenticates and a new session started. If set to "none" no change will be made. "newSession" will create a new empty session. "migrateSession" will create a new session and copy the session attributes to the new session. Defaults to "migrateSession".
attribute session-fixation-protection {"none" | "newSession" | "migrateSession" }?
session-management.attlist &=
## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.
attribute invalid-session-url {xsd:token}?
concurrency-control =
## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.
element concurrency-control {concurrency-control.attlist, empty}
concurrency-control.attlist &=
## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to "1".
attribute max-sessions {xsd:positiveInteger}?
concurrent-sessions.attlist &=
## The URL a user will be redirected to if they attempt to use a session which has been "expired" by the concurrent session controller because they have logged in again.
concurrency-control.attlist &=
## The URL a user will be redirected to if they attempt to use a session which has been "expired" because they have logged in again.
attribute expired-url {xsd:token}?
concurrent-sessions.attlist &=
concurrency-control.attlist &=
## Specifies that an exception should be raised when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session.
attribute exception-if-maximum-exceeded {boolean}?
concurrent-sessions.attlist &=
## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration
concurrency-control.attlist &=
## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.
attribute session-registry-alias {xsd:token}?
concurrent-sessions.attlist &=
## A reference to an external SessionRegistry implementation which will be used in place of the standard one.
concurrency-control.attlist &=
## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.
attribute session-registry-ref {xsd:token}?
concurrent-sessions.attlist &=
## Allows a custom session controller to be set on the internal http AuthenticationManager. If used, the session-registry-ref attribute must also be set.
attribute session-controller-ref {xsd:token}?
remember-me =
## Sets up remember-me authentication. If used with the "key" attribute (or no attributes) the cookie-only implementation will be used. Specifying "token-repository-ref" or "remember-me-data-source-ref" will use the more secure, persisten token approach.

View File

@ -10,7 +10,7 @@
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="elts-to-inline">
<xsl:text>,access-denied-handler,anonymous,concurrent-session-control,after-invocation-provider,authentication-provider,ldap-authentication-provider,user,port-mapping,openid-login,expression-handler,filter-chain,form-login,http-basic,intercept-url,logout,password-encoder,port-mappings,port-mapper,password-compare,protect,protect-pointcut,pre-post-annotation-handling,pre-invocation-advice,post-invocation-advice,invocation-attribute-factory,remember-me,salt-source,x509,</xsl:text>
<xsl:text>,access-denied-handler,anonymous,session-management,concurrency-control,after-invocation-provider,authentication-provider,ldap-authentication-provider,user,port-mapping,openid-login,expression-handler,filter-chain,form-login,http-basic,intercept-url,logout,password-encoder,port-mappings,port-mapper,password-compare,protect,protect-pointcut,pre-post-annotation-handling,pre-invocation-advice,post-invocation-advice,invocation-attribute-factory,remember-me,salt-source,x509,</xsl:text>
</xsl:variable>
<xsl:template match="xs:element">

View File

@ -3,7 +3,7 @@ package org.springframework.security.config.http;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.security.config.ConfigTestUtils.AUTH_PROVIDER_XML;
import static org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.*;
import static org.springframework.security.config.http.AuthenticationConfigBuilder.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
@ -101,8 +101,8 @@ public class HttpSecurityBeanDefinitionParserTests {
@Test
public void beanClassNamesAreCorrect() throws Exception {
assertEquals(DefaultWebSecurityExpressionHandler.class.getName(), EXPRESSION_HANDLER_CLASS);
assertEquals(ExpressionBasedFilterInvocationSecurityMetadataSource.class.getName(), EXPRESSION_FIMDS_CLASS);
assertEquals(DefaultWebSecurityExpressionHandler.class.getName(), HttpSecurityBeanDefinitionParser.EXPRESSION_HANDLER_CLASS);
assertEquals(ExpressionBasedFilterInvocationSecurityMetadataSource.class.getName(), HttpSecurityBeanDefinitionParser.EXPRESSION_FIMDS_CLASS);
assertEquals(UsernamePasswordAuthenticationProcessingFilter.class.getName(), AUTHENTICATION_PROCESSING_FILTER_CLASS);
assertEquals(OpenIDAuthenticationProcessingFilter.class.getName(), OPEN_ID_AUTHENTICATION_PROCESSING_FILTER_CLASS);
assertEquals(OpenIDAuthenticationProvider.class.getName(), OPEN_ID_AUTHENTICATION_PROVIDER_CLASS);
@ -662,7 +662,9 @@ public class HttpSecurityBeanDefinitionParserTests {
public void concurrentSessionSupportAddsFilterAndExpectedBeans() throws Exception {
setContext(
"<http auto-config='true'>" +
" <concurrent-session-control session-registry-alias='sr' expired-url='/expired'/>" +
" <session-management>" +
" <concurrency-control session-registry-alias='sr' expired-url='/expired'/>" +
" </session-management>" +
"</http>" + AUTH_PROVIDER_XML);
List<Filter> filters = getFilters("/someurl");
@ -677,40 +679,15 @@ public class HttpSecurityBeanDefinitionParserTests {
public void externalSessionRegistryBeanIsConfiguredCorrectly() throws Exception {
setContext(
"<http auto-config='true'>" +
" <concurrent-session-control session-registry-ref='sr' />" +
" <session-management>" +
" <concurrency-control session-registry-ref='sr' />" +
" </session-management>" +
"</http>" +
"<b:bean id='sr' class='" + SessionRegistryImpl.class.getName() + "'/>" +
AUTH_PROVIDER_XML);
checkSessionRegistry();
}
// @Test(expected=BeanDefinitionParsingException.class)
// public void useOfExternalConcurrentSessionControllerRequiresSessionRegistryToBeSet() throws Exception {
// setContext(
// "<http auto-config='true'>" +
// " <concurrent-session-control session-controller-ref='sc' expired-url='/expired'/>" +
// "</http>" +
// "<b:bean id='sc' class='" + ConcurrentSessionControllerImpl.class.getName() +"'>" +
// " <b:property name='sessionRegistry'>" +
// " <b:bean class='"+ SessionRegistryImpl.class.getName() + "'/>" +
// " </b:property>" +
// "</b:bean>" + AUTH_PROVIDER_XML);
// }
@Test
public void useOfExternalSessionControllerAndRegistryIsWiredCorrectly() throws Exception {
setContext(
"<http auto-config='true'>" +
" <concurrent-session-control session-registry-ref='sr' session-controller-ref='sc' expired-url='/expired'/>" +
"</http>" +
"<b:bean id='sc' class='" + ConcurrentSessionControllerImpl.class.getName() +"'>" +
" <b:property name='sessionRegistry' ref='sr'/>" +
"</b:bean>" +
"<b:bean id='sr' class='"+ SessionRegistryImpl.class.getName() + "'/>" + AUTH_PROVIDER_XML
);
checkSessionRegistry();
}
private void checkSessionRegistry() throws Exception {
Object sessionRegistry = appContext.getBean("sr");
Object sessionRegistryFromConcurrencyFilter = FieldUtils.getFieldValue(
@ -727,42 +704,14 @@ public class HttpSecurityBeanDefinitionParserTests {
// SEC-1143
assertSame(sessionRegistry, sessionRegistryFromFormLoginFilter);
}
/*
// These no longer apply with the internal authentication manager in <http> and it won't be possible to check the
// Validity of the configuration against the central AuthenticationManager when we allow more than one <http> element.
@Test(expected=BeanDefinitionParsingException.class)
public void concurrentSessionSupportCantBeUsedWithIndependentControllerBean() throws Exception {
setContext(
"<authentication-manager alias='authManager' session-controller-ref='sc'/>" +
"<http auto-config='true'>" +
" <concurrent-session-control session-registry-alias='seshRegistry' expired-url='/expired'/>" +
"</http>" +
"<b:bean id='sc' class='" + ConcurrentSessionControllerImpl.class.getName() +"'>" +
" <b:property name='sessionRegistry'>" +
" <b:bean class='"+ SessionRegistryImpl.class.getName() + "'/>" +
" </b:property>" +
"</b:bean>" + AUTH_PROVIDER_XML);
}
@Test(expected=BeanDefinitionParsingException.class)
public void concurrentSessionSupportCantBeUsedWithIndependentControllerBean2() throws Exception {
setContext(
"<http auto-config='true'>" +
" <concurrent-session-control session-registry-alias='seshRegistry' expired-url='/expired'/>" +
"</http>" +
"<b:bean id='sc' class='org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl'>" +
" <b:property name='sessionRegistry'>" +
" <b:bean class='" + SessionRegistryImpl.class.getName() + "'/>" +
" </b:property>" +
"</b:bean>" +
"<authentication-manager alias='authManager' session-controller-ref='sc'/>" + AUTH_PROVIDER_XML);
}
*/
@Test(expected=ConcurrentLoginException.class)
public void concurrentSessionMaxSessionsIsCorrectlyConfigured() throws Exception {
setContext(
"<http auto-config='true'>" +
" <concurrent-session-control max-sessions='2' exception-if-maximum-exceeded='true' />" +
" <session-management>" +
" <concurrency-control max-sessions='2' exception-if-maximum-exceeded='true' />" +
" </session-management>" +
"</http>" + AUTH_PROVIDER_XML);
SessionAuthenticationStrategy seshStrategy = (SessionAuthenticationStrategy) FieldUtils.getFieldValue(
getFilter(SessionManagementFilter.class), "sessionStrategy");
@ -830,7 +779,9 @@ public class HttpSecurityBeanDefinitionParserTests {
@Test
public void disablingSessionProtectionRemovesSessionManagementFilterIfNoInvalidSessionUrlSet() throws Exception {
setContext(
"<http auto-config='true' session-fixation-protection='none'/>" + AUTH_PROVIDER_XML);
"<http auto-config='true'>" +
" <session-management session-fixation-protection='none'/>" +
"</http>" + AUTH_PROVIDER_XML);
List<Filter> filters = getFilters("/someurl");
assertFalse(filters.get(8) instanceof SessionManagementFilter);
}
@ -838,8 +789,9 @@ public class HttpSecurityBeanDefinitionParserTests {
@Test
public void disablingSessionProtectionRetainsSessionManagementFilterInvalidSessionUrlSet() throws Exception {
setContext(
"<http auto-config='true' session-fixation-protection='none'" +
" invalid-session-url='/timeoutUrl' />" + AUTH_PROVIDER_XML);
"<http auto-config='true'>" +
" <session-management session-fixation-protection='none' invalid-session-url='/timeoutUrl'/>" +
"</http>" + AUTH_PROVIDER_XML);
List<Filter> filters = getFilters("/someurl");
Object filter = filters.get(8);
assertTrue(filter instanceof SessionManagementFilter);

View File

@ -38,21 +38,12 @@
</http>
<!--
Usernames/Passwords are
rod/koala
dianne/emu
scott/wombat
peter/opal
-->
<authentication-manager>
<authentication-provider>
<password-encoder hash="md5"/>
<user-service>
<user name="rod" password="a564de63c2d0da68cf47586ee05984d7" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
<user name="dianne" password="65d15fe9156f9c4bbffd98085992a44e" authorities="ROLE_USER,ROLE_TELLER" />
<user name="scott" password="2b58af6dddbd072ed27ffc86725d7d3a" authorities="ROLE_USER" />
<user name="peter" password="22b5c9accc6e1ba628cedc63a72d57f8" authorities="ROLE_USER" />
<user name="joe" password="password" authorities="ROLE_JANITOR" />
<user name="jim" password="password" authorities="ROLE_TRADER" />
<user name="jane" password="password" authorities="ROLE_SUPERVISOR" />
</user-service>
</authentication-provider>
</authentication-manager>

View File

@ -1,5 +1,5 @@
# Global logging configuration
log4j.rootLogger=WARN, stdout
log4j.rootLogger=DEBUG, stdout
log4j.logger.org.springframework.security=DEBUG