Skip to content

Commit 61fae60

Browse files
committed
Merge branch '7.0.x'
2 parents bb59d5c + 1eef373 commit 61fae60

3 files changed

Lines changed: 159 additions & 0 deletions

File tree

config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecuritySelector.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
@Deprecated
4040
final class GlobalMethodSecuritySelector implements ImportSelector {
4141

42+
private static final boolean isAccessPresent = ClassUtils.isPresent(
43+
"org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor", null);
44+
4245
@Override
4346
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
4447
Class<EnableGlobalMethodSecurity> annoType = EnableGlobalMethodSecurity.class;
@@ -59,16 +62,26 @@ public String[] selectImports(AnnotationMetadata importingClassMetadata) {
5962
boolean jsr250Enabled = attributes.getBoolean("jsr250Enabled");
6063
List<String> classNames = new ArrayList<>(4);
6164
if (isProxy) {
65+
assertAccessModulePresent();
6266
classNames.add(MethodSecurityMetadataSourceAdvisorRegistrar.class.getName());
6367
}
6468
classNames.add(autoProxyClassName);
6569
if (!skipMethodSecurityConfiguration) {
70+
assertAccessModulePresent();
6671
classNames.add(GlobalMethodSecurityConfiguration.class.getName());
6772
}
6873
if (jsr250Enabled) {
74+
assertAccessModulePresent();
6975
classNames.add(Jsr250MetadataSourceConfiguration.class.getName());
7076
}
7177
return classNames.toArray(new String[0]);
7278
}
7379

80+
private static void assertAccessModulePresent() {
81+
Assert.state(isAccessPresent,
82+
() -> "@EnableGlobalMethodSecurity requires the spring-security-access dependency on the "
83+
+ "classpath. Please add spring-security-access, or migrate to @EnableMethodSecurity "
84+
+ "which does not require it.");
85+
}
86+
7487
}

config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecuritySelector.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.context.annotation.ImportSelector;
2727
import org.springframework.core.type.AnnotationMetadata;
2828
import org.springframework.lang.NonNull;
29+
import org.springframework.util.Assert;
2930
import org.springframework.util.ClassUtils;
3031

3132
/**
@@ -35,6 +36,8 @@
3536
*/
3637
class ReactiveMethodSecuritySelector implements ImportSelector {
3738

39+
private static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = "org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor";
40+
3841
private static final boolean isDataPresent = ClassUtils
3942
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
4043

@@ -56,6 +59,11 @@ public String[] selectImports(AnnotationMetadata importMetadata) {
5659
imports.add(ReactiveAuthorizationManagerMethodSecurityConfiguration.class.getName());
5760
}
5861
else {
62+
Assert.state(
63+
ClassUtils.isPresent(METHOD_SECURITY_METADATA_SOURCE_ADVISOR, ClassUtils.getDefaultClassLoader()),
64+
() -> "@EnableReactiveMethodSecurity(useAuthorizationManager = false) requires the "
65+
+ "spring-security-access dependency on the classpath. Please add spring-security-access, "
66+
+ "or use the default useAuthorizationManager = true which does not require it.");
5967
imports.add(ReactiveMethodSecurityConfiguration.class.getName());
6068
}
6169
if (isDataPresent) {
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.method.configuration;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.context.annotation.AdviceMode;
22+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
23+
import org.springframework.context.annotation.Configuration;
24+
import org.springframework.security.test.support.ClassPathExclusions;
25+
26+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
27+
28+
/**
29+
* Tests for gh-19441: {@code spring-security-access} moved
30+
* {@link org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor}
31+
* out of {@code spring-security-core} and into the optional
32+
* {@code spring-security-access} module. {@link EnableMethodSecurity} and
33+
* {@link EnableReactiveMethodSecurity}'s default (AuthorizationManager-based) mode never
34+
* needed that class and continue to work without {@code spring-security-access} on the
35+
* classpath, but the deprecated legacy method security annotations do need it and
36+
* previously failed with a confusing {@link NoClassDefFoundError} instead of an
37+
* actionable message.
38+
*/
39+
@ClassPathExclusions("spring-security-access-*.jar")
40+
public class Gh19441Tests {
41+
42+
@Test
43+
public void enableMethodSecurityWhenAccessModuleAbsentThenContextStartsCleanly() {
44+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
45+
context.register(EnableMethodSecurityConfig.class);
46+
context.refresh();
47+
}
48+
}
49+
50+
@Test
51+
public void enableReactiveMethodSecurityWhenAccessModuleAbsentThenContextStartsCleanly() {
52+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
53+
context.register(EnableReactiveMethodSecurityConfig.class);
54+
context.refresh();
55+
}
56+
}
57+
58+
@Test
59+
public void enableGlobalMethodSecurityWhenProxyModeAndAccessModuleAbsentThenClearException() {
60+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
61+
context.register(EnableGlobalMethodSecurityProxyConfig.class);
62+
assertThatExceptionOfType(Exception.class).isThrownBy(context::refresh)
63+
.havingRootCause()
64+
.isInstanceOf(IllegalStateException.class)
65+
.withMessageContaining("spring-security-access");
66+
}
67+
}
68+
69+
@Test
70+
public void enableGlobalMethodSecurityWhenAspectJModeAndAccessModuleAbsentThenClearException() {
71+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
72+
context.register(EnableGlobalMethodSecurityAspectJConfig.class);
73+
assertThatExceptionOfType(Exception.class).isThrownBy(context::refresh)
74+
.havingRootCause()
75+
.isInstanceOf(IllegalStateException.class)
76+
.withMessageContaining("spring-security-access");
77+
}
78+
}
79+
80+
@Test
81+
public void enableReactiveMethodSecurityWhenUseAuthorizationManagerFalseAndAccessModuleAbsentThenClearException() {
82+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
83+
context.register(EnableReactiveMethodSecurityLegacyConfig.class);
84+
assertThatExceptionOfType(Exception.class).isThrownBy(context::refresh)
85+
.havingRootCause()
86+
.isInstanceOf(IllegalStateException.class)
87+
.withMessageContaining("spring-security-access");
88+
}
89+
}
90+
91+
@Test
92+
public void enableGlobalMethodSecurityWhenAspectJModeAndJsr250EnabledAndConfigurationSubclassedAndAccessModuleAbsentThenClearException() {
93+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
94+
context.register(EnableGlobalMethodSecurityAspectJJsr250SubclassedConfig.class);
95+
assertThatExceptionOfType(Exception.class).isThrownBy(context::refresh)
96+
.havingRootCause()
97+
.isInstanceOf(IllegalStateException.class)
98+
.withMessageContaining("spring-security-access");
99+
}
100+
}
101+
102+
@Configuration
103+
@EnableMethodSecurity
104+
static class EnableMethodSecurityConfig {
105+
106+
}
107+
108+
@Configuration
109+
@EnableReactiveMethodSecurity
110+
static class EnableReactiveMethodSecurityConfig {
111+
112+
}
113+
114+
@Configuration
115+
@EnableGlobalMethodSecurity(prePostEnabled = true)
116+
static class EnableGlobalMethodSecurityProxyConfig {
117+
118+
}
119+
120+
@Configuration
121+
@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ)
122+
static class EnableGlobalMethodSecurityAspectJConfig {
123+
124+
}
125+
126+
@Configuration
127+
@EnableReactiveMethodSecurity(useAuthorizationManager = false)
128+
static class EnableReactiveMethodSecurityLegacyConfig {
129+
130+
}
131+
132+
@Configuration
133+
@EnableGlobalMethodSecurity(jsr250Enabled = true, mode = AdviceMode.ASPECTJ)
134+
static class EnableGlobalMethodSecurityAspectJJsr250SubclassedConfig extends GlobalMethodSecurityConfiguration {
135+
136+
}
137+
138+
}

0 commit comments

Comments
 (0)