/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.http;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
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.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfLogoutHandler;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.security.web.csrf.LazyCsrfTokenRepository;
import org.springframework.security.web.csrf.MissingCsrfTokenException;
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Parser for the {@code CsrfFilter}.
*
* @author Rob Winch
* @since 3.2
*/
public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
private static final String REQUEST_DATA_VALUE_PROCESSOR = "requestDataValueProcessor";
private static final String DISPATCHER_SERVLET_CLASS_NAME = "org.springframework.web.servlet.DispatcherServlet";
private static final String ATT_MATCHER = "request-matcher-ref";
private static final String ATT_REPOSITORY = "token-repository-ref";
private String csrfRepositoryRef;
private BeanDefinition csrfFilter;
@Override
public BeanDefinition parse(Element element, ParserContext pc) {
boolean disabled = element != null
&& "true".equals(element.getAttribute("disabled"));
if (disabled) {
return null;
}
boolean webmvcPresent = ClassUtils.isPresent(DISPATCHER_SERVLET_CLASS_NAME,
getClass().getClassLoader());
if (webmvcPresent) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(
CsrfRequestDataValueProcessor.class);
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(
beanDefinition, REQUEST_DATA_VALUE_PROCESSOR);
pc.registerBeanComponent(componentDefinition);
}
String matcherRef = null;
if (element != null) {
this.csrfRepositoryRef = element.getAttribute(ATT_REPOSITORY);
matcherRef = element.getAttribute(ATT_MATCHER);
}
if (!StringUtils.hasText(this.csrfRepositoryRef)) {
RootBeanDefinition csrfTokenRepository = new RootBeanDefinition(
HttpSessionCsrfTokenRepository.class);
BeanDefinitionBuilder lazyTokenRepository = BeanDefinitionBuilder
.rootBeanDefinition(LazyCsrfTokenRepository.class);
lazyTokenRepository.addConstructorArgValue(csrfTokenRepository);
this.csrfRepositoryRef = pc.getReaderContext()
.generateBeanName(lazyTokenRepository.getBeanDefinition());
pc.registerBeanComponent(new BeanComponentDefinition(
lazyTokenRepository.getBeanDefinition(), this.csrfRepositoryRef));
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.rootBeanDefinition(CsrfFilter.class);
builder.addConstructorArgReference(this.csrfRepositoryRef);
if (StringUtils.hasText(matcherRef)) {
builder.addPropertyReference("requireCsrfProtectionMatcher", matcherRef);
}
this.csrfFilter = builder.getBeanDefinition();
return this.csrfFilter;
}
/**
* Populate the AccessDeniedHandler on the {@link CsrfFilter}
*
* @param invalidSessionStrategy the {@link InvalidSessionStrategy} to use
* @param defaultDeniedHandler the {@link AccessDeniedHandler} to use
*/
void initAccessDeniedHandler(BeanDefinition invalidSessionStrategy,
BeanMetadataElement defaultDeniedHandler) {
BeanMetadataElement accessDeniedHandler = createAccessDeniedHandler(
invalidSessionStrategy, defaultDeniedHandler);
this.csrfFilter.getPropertyValues().addPropertyValue("accessDeniedHandler",
accessDeniedHandler);
}
/**
* Creates the {@link AccessDeniedHandler} from the result of
* {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)} and
* {@link #getInvalidSessionStrategy(HttpSecurityBuilder)}. If
* {@link #getInvalidSessionStrategy(HttpSecurityBuilder)} is non-null, then a
* {@link DelegatingAccessDeniedHandler} is used in combination with
* {@link InvalidSessionAccessDeniedHandler} and the
* {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)}. Otherwise, only
* {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)} is used.
*
* @param invalidSessionStrategy the {@link InvalidSessionStrategy} to use
* @param defaultDeniedHandler the {@link AccessDeniedHandler} to use
*
* @return the {@link BeanMetadataElement} that is the {@link AccessDeniedHandler} to
* populate on the {@link CsrfFilter}
*/
private BeanMetadataElement createAccessDeniedHandler(
BeanDefinition invalidSessionStrategy,
BeanMetadataElement defaultDeniedHandler) {
if (invalidSessionStrategy == null) {
return defaultDeniedHandler;
}
ManagedMap<Class<? extends AccessDeniedException>, BeanDefinition> handlers = new ManagedMap<Class<? extends AccessDeniedException>, BeanDefinition>();
BeanDefinitionBuilder invalidSessionHandlerBldr = BeanDefinitionBuilder
.rootBeanDefinition(InvalidSessionAccessDeniedHandler.class);
invalidSessionHandlerBldr.addConstructorArgValue(invalidSessionStrategy);
handlers.put(MissingCsrfTokenException.class,
invalidSessionHandlerBldr.getBeanDefinition());
BeanDefinitionBuilder deniedBldr = BeanDefinitionBuilder
.rootBeanDefinition(DelegatingAccessDeniedHandler.class);
deniedBldr.addConstructorArgValue(handlers);
deniedBldr.addConstructorArgValue(defaultDeniedHandler);
return deniedBldr.getBeanDefinition();
}
BeanDefinition getCsrfAuthenticationStrategy() {
BeanDefinitionBuilder csrfAuthenticationStrategy = BeanDefinitionBuilder
.rootBeanDefinition(CsrfAuthenticationStrategy.class);
csrfAuthenticationStrategy.addConstructorArgReference(this.csrfRepositoryRef);
return csrfAuthenticationStrategy.getBeanDefinition();
}
BeanDefinition getCsrfLogoutHandler() {
BeanDefinitionBuilder csrfAuthenticationStrategy = BeanDefinitionBuilder
.rootBeanDefinition(CsrfLogoutHandler.class);
csrfAuthenticationStrategy.addConstructorArgReference(this.csrfRepositoryRef);
return csrfAuthenticationStrategy.getBeanDefinition();
}
}