Skip to content

Commit bc75585

Browse files
Use lock for preventing ConcurrentModificationException
Signed-off-by: Tran Ngoc Nhan <[email protected]>
1 parent 6159e08 commit bc75585

File tree

2 files changed

+29
-6
lines changed

2 files changed

+29
-6
lines changed

core/src/main/java/org/springframework/security/authorization/method/AuthorizationAdvisorProxyFactory.java

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
3535
import java.util.SortedSet;
3636
import java.util.TreeMap;
3737
import java.util.TreeSet;
38+
import java.util.concurrent.locks.ReentrantLock;
3839
import java.util.function.Supplier;
3940
import java.util.stream.Stream;
4041

@@ -77,6 +78,7 @@
7778
* </pre>
7879
*
7980
* @author Josh Cummings
81+
* @author Ngoc Nhan
8082
* @since 6.3
8183
*/
8284
public final class AuthorizationAdvisorProxyFactory
@@ -97,6 +99,8 @@ public final class AuthorizationAdvisorProxyFactory
9799

98100
private TargetVisitor visitor = DEFAULT_VISITOR;
99101

102+
private final ReentrantLock reentrantLock = new ReentrantLock();
103+
100104
/**
101105
* Construct an {@link AuthorizationAdvisorProxyFactory} with the provided advisors.
102106
*
@@ -165,7 +169,13 @@ public static AuthorizationAdvisorProxyFactory withReactiveDefaults() {
165169
*/
166170
@Override
167171
public Object proxy(Object target) {
168-
AnnotationAwareOrderComparator.sort(this.advisors);
172+
try {
173+
this.reentrantLock.lock();
174+
AnnotationAwareOrderComparator.sort(this.advisors);
175+
}
176+
finally {
177+
this.reentrantLock.unlock();
178+
}
169179
if (target == null) {
170180
return null;
171181
}
@@ -178,9 +188,7 @@ public Object proxy(Object target) {
178188
}
179189
ProxyFactory factory = new ProxyFactory(target);
180190
factory.addAdvisors(this.authorizationProxy);
181-
for (Advisor advisor : this.advisors) {
182-
factory.addAdvisors(advisor);
183-
}
191+
factory.addAdvisors(this.advisors.toArray(Advisor[]::new));
184192
factory.addInterface(AuthorizationProxy.class);
185193
factory.setOpaque(true);
186194
factory.setProxyTargetClass(!Modifier.isFinal(target.getClass().getModifiers()));

core/src/test/java/org/springframework/security/authorization/AuthorizationAdvisorProxyFactoryTests.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
3131
import java.util.SortedSet;
3232
import java.util.TreeMap;
3333
import java.util.TreeSet;
34+
import java.util.concurrent.CompletableFuture;
3435
import java.util.function.Supplier;
3536
import java.util.stream.Stream;
3637

@@ -52,6 +53,7 @@
5253

5354
import static org.assertj.core.api.Assertions.assertThat;
5455
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
56+
import static org.assertj.core.api.Assertions.assertThatNoException;
5557
import static org.mockito.ArgumentMatchers.any;
5658
import static org.mockito.BDDMockito.given;
5759
import static org.mockito.Mockito.atLeastOnce;
@@ -360,6 +362,19 @@ public void proxyWhenDefaultsThenInstanceOfAuthorizationProxy() {
360362
assertThat(target).isSameAs(this.flight);
361363
}
362364

365+
@Test
366+
public void asyncProxyWhenDefaultsThenNoException() {
367+
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
368+
List<CompletableFuture<Void>> futures = new ArrayList<>();
369+
370+
for (int i = 0; i < 1000; i++) {
371+
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> proxy(factory, this.flight));
372+
futures.add(future);
373+
}
374+
375+
assertThatNoException().isThrownBy(() -> futures.forEach(CompletableFuture::join));
376+
}
377+
363378
private Authentication authenticated(String user, String... authorities) {
364379
return TestAuthentication.authenticated(TestAuthentication.withUsername(user).authorities(authorities).build());
365380
}

0 commit comments

Comments
 (0)