浏览代码

PasswordEncoder as Bean default for XML

Issue: gh-4873
Rob Winch 7 年之前
父节点
当前提交
9afee9e4e2

+ 24 - 13
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.<AuthenticationProvider> asList(provider));
-				}
-				catch (NoSuchBeanDefinitionException noUds) {
-				}
+			if (!BeanIds.AUTHENTICATION_MANAGER.equals(e.getBeanName())) {
+				throw e;
+			}
+
+			UserDetailsService uds = getBeanOrNull(UserDetailsService.class);
+			if(uds == null) {
 				throw new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER,
-						MISSING_BEAN_ERROR_MESSAGE);
+					MISSING_BEAN_ERROR_MESSAGE);
+			}
+
+			DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
+			provider.setUserDetailsService(uds);
+			PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
+			if (passwordEncoder != null) {
+				provider.setPasswordEncoder(passwordEncoder);
 			}
-			throw e;
+			provider.afterPropertiesSet();
+			return new ProviderManager(Arrays.<AuthenticationProvider> asList(provider));
 		}
 	}
 
@@ -79,4 +83,11 @@ public class AuthenticationManagerFactoryBean implements
 		bf = beanFactory;
 	}
 
+	private <T> T getBeanOrNull(Class<T> type) {
+		try {
+			return this.bf.getBean(type);
+		} catch (NoSuchBeanDefinitionException noUds) {
+			return null;
+		}
+	}
 }

+ 8 - 8
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,

+ 6 - 0
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;
 

+ 29 - 9
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 {
 			+ "    <authentication-provider>"
 			+ "        <user-service>"
 			+ "            <user name='bob' password='{noop}bobspassword' authorities='ROLE_A,ROLE_B' />"
-			+ "        </user-service>" + "    </authentication-provider>"
+			+ "        </user-service>"
+			+ "    </authentication-provider>"
 			+ "</authentication-manager>";
 	@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("<b:bean id='passwordEncoder' class='org.springframework.security.crypto.password.NoOpPasswordEncoder' factory-method='getInstance'/>"
+			+ "<user-service>"
+			+ "  <user name='user' password='password' authorities='ROLE_A,ROLE_B' />"
+			+ "</user-service>"
+			+ "<http/>")
+			.mockMvcAfterSpringSecurityOk()
+			.autowire();
+
+		this.mockMvc.perform(get("/").with(httpBasic("user", "password")))
+			.andExpect(status().isOk());
+	}
+
 	private static class AuthListener implements
 			ApplicationListener<AbstractAuthenticationEvent> {
 		List<AbstractAuthenticationEvent> events = new ArrayList<AbstractAuthenticationEvent>();

+ 60 - 0
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());
+	}
+
+}

+ 34 - 0
config/src/test/resources/org/springframework/security/config/authentication/PasswordEncoderParserTests-bean.xml

@@ -0,0 +1,34 @@
+<!--
+  ~ 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.
+  -->
+
+<b:beans xmlns="http://www.springframework.org/schema/security"
+		 xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+						http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
+
+	<b:bean id="passwordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder" factory-method="getInstance"/>
+
+	<http />
+
+	<authentication-manager>
+		<authentication-provider>
+			<user-service>
+				<user name="user" password="password" authorities="ROLE_USER" />
+			</user-service>
+		</authentication-provider>
+	</authentication-manager>
+</b:beans>

+ 15 - 0
config/src/test/resources/org/springframework/security/config/authentication/PasswordEncoderParserTests-default.xml

@@ -0,0 +1,15 @@
+<b:beans xmlns="http://www.springframework.org/schema/security"
+		 xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+						http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
+	<http />
+
+	<authentication-manager>
+		<authentication-provider>
+			<user-service>
+				<user name="user" password="{noop}password" authorities="ROLE_USER" />
+			</user-service>
+		</authentication-provider>
+	</authentication-manager>
+</b:beans>