浏览代码

Set "rolePrefix" in ReactiveMethodSecurityConfiguration

Currently, `GrantedAuthorityDefaults` is not considered in
`ReactiveMethodSecurityConfiguration`.
This commit updates the configuration to be aware of
`GrantedAuthorityDefaults` and update `rolePrefix` when the bean is
available.

Also, use the same instance of `DefaultMethodSecurityExpressionHandler`
when constructing `ExpressionBasedAnnotationAttributeFactory`.
Tadaya Tsuyukubo 6 年之前
父节点
当前提交
aef3f514fe

+ 19 - 4
config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfiguration.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -16,6 +16,7 @@
 
 package org.springframework.security.config.annotation.method.configuration;
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -28,17 +29,21 @@ import org.springframework.security.access.method.AbstractMethodSecurityMetadata
 import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource;
 import org.springframework.security.access.prepost.PrePostAdviceReactiveMethodInterceptor;
 import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;
+import org.springframework.security.config.core.GrantedAuthorityDefaults;
 
 import java.util.Arrays;
 
 /**
  * @author Rob Winch
+ * @author Tadaya Tsuyukubo
  * @since 5.0
  */
 @Configuration
 class ReactiveMethodSecurityConfiguration implements ImportAware {
 	private int advisorOrder;
 
+	private GrantedAuthorityDefaults grantedAuthorityDefaults;
+
 	@Bean
 	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 	public MethodSecurityMetadataSourceAdvisor methodSecurityInterceptor(AbstractMethodSecurityMetadataSource source) throws Exception {
@@ -49,9 +54,9 @@ class ReactiveMethodSecurityConfiguration implements ImportAware {
 	}
 
 	@Bean
-	public DelegatingMethodSecurityMetadataSource methodMetadataSource() {
+	public DelegatingMethodSecurityMetadataSource methodMetadataSource(MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
 		ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory(
-				new DefaultMethodSecurityExpressionHandler());
+				methodSecurityExpressionHandler);
 		PrePostAnnotationSecurityMetadataSource prePostSource = new PrePostAnnotationSecurityMetadataSource(
 			attributeFactory);
 		return new DelegatingMethodSecurityMetadataSource(Arrays.asList(prePostSource));
@@ -70,11 +75,21 @@ class ReactiveMethodSecurityConfiguration implements ImportAware {
 
 	@Bean
 	public DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler() {
-		return new DefaultMethodSecurityExpressionHandler();
+		DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
+		if (this.grantedAuthorityDefaults != null) {
+			handler.setDefaultRolePrefix(this.grantedAuthorityDefaults.getRolePrefix());
+		}
+		return handler;
 	}
 
 	@Override
 	public void setImportMetadata(AnnotationMetadata importMetadata) {
 		this.advisorOrder = (int) importMetadata.getAnnotationAttributes(EnableReactiveMethodSecurity.class.getName()).get("order");
 	}
+
+	@Autowired(required = false)
+	void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) {
+		this.grantedAuthorityDefaults = grantedAuthorityDefaults;
+	}
+
 }

+ 91 - 0
config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfigurationTests.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright 2002-2019 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
+ *
+ *      https://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.annotation.method.configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.security.access.expression.SecurityExpressionRoot;
+import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
+import org.springframework.security.access.intercept.method.MockMethodInvocation;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.config.core.GrantedAuthorityDefaults;
+import org.springframework.security.config.test.SpringTestRule;
+
+/**
+ * @author Tadaya Tsuyukubo
+ */
+public class ReactiveMethodSecurityConfigurationTests {
+
+	@Rule
+	public final SpringTestRule spring = new SpringTestRule();
+
+	@Autowired
+	DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler;
+
+	@Test
+	public void rolePrefixWithGrantedAuthorityDefaults() {
+		this.spring.register(WithRolePrefixConfiguration.class).autowire();
+
+		TestingAuthenticationToken authentication = new TestingAuthenticationToken(
+				"principal", "credential", "CUSTOM_ABC");
+		MockMethodInvocation methodInvocation = mock(MockMethodInvocation.class);
+
+		EvaluationContext context = this.methodSecurityExpressionHandler
+				.createEvaluationContext(authentication, methodInvocation);
+		SecurityExpressionRoot root = (SecurityExpressionRoot) context.getRootObject()
+				.getValue();
+
+		assertThat(root.hasRole("ROLE_ABC")).isFalse();
+		assertThat(root.hasRole("ROLE_CUSTOM_ABC")).isFalse();
+		assertThat(root.hasRole("CUSTOM_ABC")).isTrue();
+		assertThat(root.hasRole("ABC")).isTrue();
+	}
+
+	@Test
+	public void rolePrefixWithDefaultConfig() {
+		this.spring.register(ReactiveMethodSecurityConfiguration.class).autowire();
+
+		TestingAuthenticationToken authentication = new TestingAuthenticationToken(
+				"principal", "credential", "ROLE_ABC");
+		MockMethodInvocation methodInvocation = mock(MockMethodInvocation.class);
+
+		EvaluationContext context = this.methodSecurityExpressionHandler
+				.createEvaluationContext(authentication, methodInvocation);
+		SecurityExpressionRoot root = (SecurityExpressionRoot) context.getRootObject()
+				.getValue();
+
+		assertThat(root.hasRole("ROLE_ABC")).isTrue();
+		assertThat(root.hasRole("ABC")).isTrue();
+	}
+
+	@Configuration
+	@EnableReactiveMethodSecurity // this imports ReactiveMethodSecurityConfiguration
+	static class WithRolePrefixConfiguration {
+		@Bean
+		GrantedAuthorityDefaults grantedAuthorityDefaults() {
+			return new GrantedAuthorityDefaults("CUSTOM_");
+		}
+	}
+
+}