From 9afee9e4e200411f32cc0491a1d98c225ad5fec9 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Tue, 21 Nov 2017 15:31:14 -0600 Subject: [PATCH] PasswordEncoder as Bean default for XML Issue: gh-4873 --- .../AuthenticationManagerFactoryBean.java | 39 +++++++----- ...nticationProviderBeanDefinitionParser.java | 16 ++--- .../authentication/PasswordEncoderParser.java | 6 ++ ...ationManagerBeanDefinitionParserTests.java | 38 +++++++++--- .../PasswordEncoderParserTests.java | 60 +++++++++++++++++++ .../PasswordEncoderParserTests-bean.xml | 34 +++++++++++ .../PasswordEncoderParserTests-default.xml | 15 +++++ 7 files changed, 177 insertions(+), 31 deletions(-) create mode 100644 config/src/test/java/org/springframework/security/config/authentication/PasswordEncoderParserTests.java create mode 100644 config/src/test/resources/org/springframework/security/config/authentication/PasswordEncoderParserTests-bean.xml create mode 100644 config/src/test/resources/org/springframework/security/config/authentication/PasswordEncoderParserTests-default.xml diff --git a/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java b/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java index 9376929099..cfc14fcca8 100644 --- a/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java +++ b/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java @@ -26,6 +26,7 @@ import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.BeanIds; import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.PasswordEncoder; import java.util.Arrays; @@ -49,21 +50,24 @@ public class AuthenticationManagerFactoryBean implements return (AuthenticationManager) bf.getBean(BeanIds.AUTHENTICATION_MANAGER); } catch (NoSuchBeanDefinitionException e) { - if (BeanIds.AUTHENTICATION_MANAGER.equals(e.getBeanName())) { - try { - UserDetailsService uds = bf.getBean(UserDetailsService.class); - DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); - provider.setUserDetailsService(uds); - provider.afterPropertiesSet(); - return new ProviderManager( - Arrays. asList(provider)); - } - catch (NoSuchBeanDefinitionException noUds) { - } - throw new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER, - MISSING_BEAN_ERROR_MESSAGE); + if (!BeanIds.AUTHENTICATION_MANAGER.equals(e.getBeanName())) { + throw e; } - throw e; + + UserDetailsService uds = getBeanOrNull(UserDetailsService.class); + if(uds == null) { + throw new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER, + MISSING_BEAN_ERROR_MESSAGE); + } + + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setUserDetailsService(uds); + PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class); + if (passwordEncoder != null) { + provider.setPasswordEncoder(passwordEncoder); + } + provider.afterPropertiesSet(); + return new ProviderManager(Arrays. asList(provider)); } } @@ -79,4 +83,11 @@ public class AuthenticationManagerFactoryBean implements bf = beanFactory; } + private T getBeanOrNull(Class type) { + try { + return this.bf.getBean(type); + } catch (NoSuchBeanDefinitionException noUds) { + return null; + } + } } diff --git a/config/src/main/java/org/springframework/security/config/authentication/AuthenticationProviderBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/authentication/AuthenticationProviderBeanDefinitionParser.java index a044afdff6..343acbe65f 100644 --- a/config/src/main/java/org/springframework/security/config/authentication/AuthenticationProviderBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/authentication/AuthenticationProviderBeanDefinitionParser.java @@ -15,6 +15,7 @@ */ package org.springframework.security.config.authentication; +import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -36,17 +37,16 @@ public class AuthenticationProviderBeanDefinitionParser implements BeanDefinitio private static final String ATT_USER_DETAILS_REF = "user-service-ref"; public BeanDefinition parse(Element element, ParserContext pc) { - RootBeanDefinition authProvider = new RootBeanDefinition( - DaoAuthenticationProvider.class); + RootBeanDefinition authProvider = new RootBeanDefinition(DaoAuthenticationProvider.class); authProvider.setSource(pc.extractSource(element)); - Element passwordEncoderElt = DomUtils.getChildElementByTagName(element, - Elements.PASSWORD_ENCODER); + Element passwordEncoderElt = DomUtils.getChildElementByTagName(element, Elements.PASSWORD_ENCODER); - if (passwordEncoderElt != null) { - PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElt, pc); - authProvider.getPropertyValues().addPropertyValue("passwordEncoder", - pep.getPasswordEncoder()); + PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElt, pc); + BeanMetadataElement passwordEncoder = pep.getPasswordEncoder(); + if (passwordEncoder != null) { + authProvider.getPropertyValues() + .addPropertyValue("passwordEncoder", passwordEncoder); } Element userServiceElt = DomUtils.getChildElementByTagName(element, diff --git a/config/src/main/java/org/springframework/security/config/authentication/PasswordEncoderParser.java b/config/src/main/java/org/springframework/security/config/authentication/PasswordEncoderParser.java index 4e840408ca..c85d738101 100644 --- a/config/src/main/java/org/springframework/security/config/authentication/PasswordEncoderParser.java +++ b/config/src/main/java/org/springframework/security/config/authentication/PasswordEncoderParser.java @@ -56,6 +56,12 @@ public class PasswordEncoderParser { } private void parse(Element element, ParserContext parserContext) { + if (element == null) { + if (parserContext.getRegistry().containsBeanDefinition("passwordEncoder")) { + this.passwordEncoder = parserContext.getRegistry().getBeanDefinition("passwordEncoder"); + } + return; + } String hash = element.getAttribute(ATT_HASH); boolean useBase64 = false; diff --git a/config/src/test/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParserTests.java index 33e4c3df22..89059e255b 100644 --- a/config/src/test/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParserTests.java @@ -15,25 +15,27 @@ */ package org.springframework.security.config.authentication; -import static org.assertj.core.api.Assertions.*; - -import java.util.ArrayList; -import java.util.List; - import org.junit.Rule; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.AbstractXmlApplicationContext; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.event.AbstractAuthenticationEvent; -import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestRule; -import org.springframework.security.config.util.InMemoryXmlApplicationContext; import org.springframework.security.util.FieldUtils; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** * @@ -44,7 +46,8 @@ public class AuthenticationManagerBeanDefinitionParserTests { + " " + " " + " " - + " " + " " + + " " + + " " + ""; @Rule public final SpringTestRule spring = new SpringTestRule(); @@ -92,6 +95,23 @@ public class AuthenticationManagerBeanDefinitionParserTests { assertThat(pm.isEraseCredentialsAfterAuthentication()).isFalse(); } + @Autowired + MockMvc mockMvc; + + @Test + public void passwordEncoderBeanUsed() throws Exception { + this.spring.context("" + + "" + + " " + + "" + + "") + .mockMvcAfterSpringSecurityOk() + .autowire(); + + this.mockMvc.perform(get("/").with(httpBasic("user", "password"))) + .andExpect(status().isOk()); + } + private static class AuthListener implements ApplicationListener { List events = new ArrayList(); diff --git a/config/src/test/java/org/springframework/security/config/authentication/PasswordEncoderParserTests.java b/config/src/test/java/org/springframework/security/config/authentication/PasswordEncoderParserTests.java new file mode 100644 index 0000000000..9c7cfec860 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/authentication/PasswordEncoderParserTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2017 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.authentication; + +import org.junit.Rule; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Rob Winch + * @since 5.0 + */ +public class PasswordEncoderParserTests { + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired + MockMvc mockMvc; + + @Test + public void passwordEncoderDefaultsToDelegatingPasswordEncoder() throws Exception { + this.spring.configLocations("classpath:org/springframework/security/config/authentication/PasswordEncoderParserTests-default.xml") + .mockMvcAfterSpringSecurityOk() + .autowire(); + + this.mockMvc.perform(get("/").with(httpBasic("user", "password"))) + .andExpect(status().isOk()); + } + + @Test + public void passwordEncoderDefaultsToPasswordEncoderBean() throws Exception { + this.spring.configLocations("classpath:org/springframework/security/config/authentication/PasswordEncoderParserTests-bean.xml") + .mockMvcAfterSpringSecurityOk() + .autowire(); + + this.mockMvc.perform(get("/").with(httpBasic("user", "password"))) + .andExpect(status().isOk()); + } + +} diff --git a/config/src/test/resources/org/springframework/security/config/authentication/PasswordEncoderParserTests-bean.xml b/config/src/test/resources/org/springframework/security/config/authentication/PasswordEncoderParserTests-bean.xml new file mode 100644 index 0000000000..a72c76fa2e --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/authentication/PasswordEncoderParserTests-bean.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + diff --git a/config/src/test/resources/org/springframework/security/config/authentication/PasswordEncoderParserTests-default.xml b/config/src/test/resources/org/springframework/security/config/authentication/PasswordEncoderParserTests-default.xml new file mode 100644 index 0000000000..0f89ca231e --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/authentication/PasswordEncoderParserTests-default.xml @@ -0,0 +1,15 @@ + + + + + + + + + + +