/* * 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(); } }