Browse Source

Fix defaultMethodExpressionHandler autowiring

Previously if a Bean for GlobalMethodSecurityConfiguration's
defaultMethodExpressionHandler was found on a Configuration that also
@Autowired a Bean that enabled method security, the Bean that was
@Autowired would not have security enabled.

This fixes the issue by delaying the lookup of Beans populated on
GlobalMethodSecurityConfiguration's defaultMethodExpressionHandler.

Fixes gh-4020
Rob Winch 9 năm trước cách đây
mục cha
commit
28278eab89

+ 26 - 20
config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java

@@ -150,6 +150,32 @@ public class GlobalMethodSecurityConfiguration
 		catch (Exception e) {
 			throw new RuntimeException(e);
 		}
+
+		PermissionEvaluator permissionEvaluator = getSingleBeanOrNull(
+				PermissionEvaluator.class);
+		if (permissionEvaluator != null) {
+			this.defaultMethodExpressionHandler
+					.setPermissionEvaluator(permissionEvaluator);
+		}
+
+		RoleHierarchy roleHierarchy = getSingleBeanOrNull(RoleHierarchy.class);
+		if (roleHierarchy != null) {
+			this.defaultMethodExpressionHandler.setRoleHierarchy(roleHierarchy);
+		}
+
+		AuthenticationTrustResolver trustResolver = getSingleBeanOrNull(
+				AuthenticationTrustResolver.class);
+		if (trustResolver != null) {
+			this.defaultMethodExpressionHandler.setTrustResolver(trustResolver);
+		}
+	}
+
+	private <T> T getSingleBeanOrNull(Class<T> type) {
+		String[] beanNamesForType = this.context.getBeanNamesForType(type);
+		if (beanNamesForType == null || beanNamesForType.length != 1) {
+			return null;
+		}
+		return this.context.getBean(beanNamesForType[0], type);
 	}
 
 	private void initializeMethodSecurityInterceptor() throws Exception {
@@ -357,16 +383,6 @@ public class GlobalMethodSecurityConfiguration
 		enableMethodSecurity = AnnotationAttributes.fromMap(annotationAttributes);
 	}
 
-	@Autowired(required = false)
-	public void setAuthenticationTrustResolver(AuthenticationTrustResolver trustResolver) {
-		this.defaultMethodExpressionHandler.setTrustResolver(trustResolver);
-	}
-
-	@Autowired(required = false)
-	public void setRoleHierarchy(RoleHierarchy roleHierarchy){
-		this.defaultMethodExpressionHandler.setRoleHierarchy(roleHierarchy);
-	}
-
 	@Autowired(required = false)
 	public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
 		this.objectPostProcessor = objectPostProcessor;
@@ -380,16 +396,6 @@ public class GlobalMethodSecurityConfiguration
 		this.jsr250MethodSecurityMetadataSource = jsr250MethodSecurityMetadataSource;
 	}
 
-	@Autowired(required = false)
-	public void setPermissionEvaluator(List<PermissionEvaluator> permissionEvaluators) {
-		if (permissionEvaluators.size() != 1) {
-			logger.debug("Not autwiring PermissionEvaluator since size != 1. Got "
-					+ permissionEvaluators);
-		}
-		this.defaultMethodExpressionHandler.setPermissionEvaluator(permissionEvaluators
-				.get(0));
-	}
-
 	@Autowired(required = false)
 	public void setMethodSecurityExpressionHandler(
 			List<MethodSecurityExpressionHandler> handlers) {

+ 86 - 0
config/src/test/java/org/springframework/security/config/method/configuration/Gh4020GlobalMethodSecurityConfigurationTests.java

@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012-2016 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.method.configuration;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.access.PermissionEvaluator;
+import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
+import org.springframework.security.authentication.AuthenticationTrustResolver;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static org.mockito.Mockito.mock;
+
+/**
+ * @author Rob Winch
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+public class Gh4020GlobalMethodSecurityConfigurationTests {
+	@Autowired
+	DenyAllService denyAll;
+
+	// gh-4020
+	@Test(expected = AuthenticationCredentialsNotFoundException.class)
+	public void denyAll() {
+		this.denyAll.denyAll();
+	}
+
+	@Configuration
+	@EnableGlobalMethodSecurity(prePostEnabled = true)
+	static class SecurityConfig {
+		@Bean
+		PermissionEvaluator permissionEvaluator() {
+			return mock(PermissionEvaluator.class);
+		}
+
+		@Bean
+		RoleHierarchy RoleHierarchy() {
+			return mock(RoleHierarchy.class);
+		}
+
+		@Bean
+		AuthenticationTrustResolver trustResolver() {
+			return mock(AuthenticationTrustResolver.class);
+		}
+
+		@Autowired
+		DenyAllService denyAll;
+	}
+
+	@Configuration
+	static class ServiceConfig {
+		@Bean
+		DenyAllService denyAllService() {
+			return new DenyAllService();
+		}
+	}
+
+	@PreAuthorize("denyAll")
+	static class DenyAllService {
+		void denyAll() {
+		}
+	}
+}