Skip to content

Commit 1e46171

Browse files
committed
Add PathPattern opt-in
Signed-off-by: Pat McCusker <[email protected]>
1 parent 9e2b3d9 commit 1e46171

File tree

7 files changed

+270
-196
lines changed

7 files changed

+270
-196
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/socket/MessageMatcherAuthorizationManagerConfiguration.java

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.springframework.context.annotation.Scope;
2222
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
2323
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
24+
import org.springframework.security.messaging.util.matcher.MessageMatcherFactory;
2425
import org.springframework.util.AntPathMatcher;
2526

2627
final class MessageMatcherAuthorizationManagerConfiguration {
@@ -29,6 +30,7 @@ final class MessageMatcherAuthorizationManagerConfiguration {
2930
@Scope("prototype")
3031
MessageMatcherDelegatingAuthorizationManager.Builder messageAuthorizationManagerBuilder(
3132
ApplicationContext context) {
33+
MessageMatcherFactory.setApplicationContext(context);
3234
return MessageMatcherDelegatingAuthorizationManager.builder()
3335
.simpDestPathMatcher(
3436
() -> (context.getBeanNamesForType(SimpAnnotationMethodMessageHandler.class).length > 0)

config/src/test/java/org/springframework/security/config/annotation/web/socket/WebSocketMessageBrokerSecurityConfigurationDocTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2025 the original author or authors.
2+
* Copyright 2002-2022 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.

messaging/src/main/java/org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManager.java

+37-159
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.apache.commons.logging.LogFactory;
2727

2828
import org.springframework.core.log.LogMessage;
29-
import org.springframework.http.server.PathContainer;
3029
import org.springframework.messaging.Message;
3130
import org.springframework.messaging.simp.SimpMessageType;
3231
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
@@ -36,14 +35,15 @@
3635
import org.springframework.security.authorization.SingleResultAuthorizationManager;
3736
import org.springframework.security.core.Authentication;
3837
import org.springframework.security.messaging.util.matcher.MessageMatcher;
38+
import org.springframework.security.messaging.util.matcher.MessageMatcherFactory;
3939
import org.springframework.security.messaging.util.matcher.PathPatternMessageMatcher;
4040
import org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher;
4141
import org.springframework.security.messaging.util.matcher.SimpMessageTypeMatcher;
4242
import org.springframework.util.AntPathMatcher;
4343
import org.springframework.util.Assert;
44+
import org.springframework.util.CollectionUtils;
4445
import org.springframework.util.PathMatcher;
4546
import org.springframework.util.function.SingletonSupplier;
46-
import org.springframework.web.util.pattern.PathPatternParser;
4747

4848
public final class MessageMatcherDelegatingAuthorizationManager implements AuthorizationManager<Message<?>> {
4949

@@ -89,13 +89,16 @@ public AuthorizationDecision check(Supplier<Authentication> authentication, Mess
8989
}
9090

9191
private MessageAuthorizationContext<?> authorizationContext(MessageMatcher<?> matcher, Message<?> message) {
92-
if (!matcher.matches((Message) message)) {
92+
MessageMatcher.MatchResult matchResult = matcher.matcher((Message) message);
93+
if (!matchResult.isMatch()) {
9394
return null;
9495
}
95-
if (matcher instanceof Builder.LazySimpDestinationMessageMatcher pathMatcher) {
96-
return new MessageAuthorizationContext<>(message, pathMatcher.extractPathVariables(message));
96+
97+
if (!CollectionUtils.isEmpty(matchResult.getVariables())) {
98+
return new MessageAuthorizationContext<>(message, matchResult.getVariables());
9799
}
98-
if (matcher instanceof Builder.LazySimpDestinationPatternMessageMatcher pathMatcher) {
100+
101+
if (matcher instanceof Builder.LazySimpDestinationMessageMatcher pathMatcher) {
99102
return new MessageAuthorizationContext<>(message, pathMatcher.extractPathVariables(message));
100103
}
101104
return new MessageAuthorizationContext<>(message);
@@ -119,8 +122,6 @@ public static final class Builder {
119122
@Deprecated
120123
private Supplier<PathMatcher> pathMatcher = AntPathMatcher::new;
121124

122-
private boolean useHttpPathSeparator = true;
123-
124125
public Builder() {
125126
}
126127

@@ -157,159 +158,70 @@ public Builder.Constraint simpTypeMatchers(SimpMessageType... typesToMatch) {
157158
}
158159

159160
/**
160-
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances without
161-
* regard to the {@link SimpMessageType}. If no destination is found on the
162-
* Message, then the Matcher returns false.
163-
* @param patterns the patterns to create
164-
* {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
165-
* from.
166-
* @deprecated use {@link #destinationPathPatterns(String...)}
161+
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} (or
162+
* {@link PathPatternMessageMatcher} if the application has configured a
163+
* {@link org.springframework.security.messaging.util.matcher.PathPatternMessageMatcherBuilderFactoryBean})
164+
* instances without regard to the {@link SimpMessageType}. If no destination is
165+
* found on the Message, then the Matcher returns false.
166+
* @param patterns the patterns to create {@code MessageMatcher}s from.
167167
*/
168-
@Deprecated
169168
public Builder.Constraint simpDestMatchers(String... patterns) {
170169
return simpDestMatchers(null, patterns);
171170
}
172171

173172
/**
174-
* Allows the creation of a security {@link Constraint} applying to messages whose
175-
* destinations match the provided {@code patterns}.
176-
* <p>
177-
* The matching of each pattern is performed by a
178-
* {@link PathPatternMessageMatcher} instance that matches irrespectively of
179-
* {@link SimpMessageType}. If no destination is found on the {@code Message},
180-
* then each {@code Matcher} returns false.
181-
* </p>
182-
* @param patterns the destination path patterns to which the security
183-
* {@code Constraint} will be applicable
184-
* @since 6.5
173+
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} (or
174+
* {@link PathPatternMessageMatcher} if the application has configured a
175+
* {@link org.springframework.security.messaging.util.matcher.PathPatternMessageMatcherBuilderFactoryBean})
176+
* instances that match on {@code SimpMessageType.MESSAGE}. If no destination is
177+
* found on the Message, then the Matcher returns false.
178+
* @param patterns the patterns to create {@code MessageMatcher}s from.
185179
*/
186-
public Builder.Constraint destinationPathPatterns(String... patterns) {
187-
return destinationPathPatterns(null, patterns);
188-
}
189-
190-
/**
191-
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that
192-
* match on {@code SimpMessageType.MESSAGE}. If no destination is found on the
193-
* Message, then the Matcher returns false.
194-
* @param patterns the patterns to create
195-
* {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
196-
* from.
197-
* @deprecated use {@link #destinationPathPatterns(String...)}
198-
*/
199-
@Deprecated
200180
public Builder.Constraint simpMessageDestMatchers(String... patterns) {
201181
return simpDestMatchers(SimpMessageType.MESSAGE, patterns);
202182
}
203183

204184
/**
205-
* Allows the creation of a security {@link Constraint} applying to messages of
206-
* the type {@code SimpMessageType.MESSAGE} whose destinations match the provided
207-
* {@code patterns}.
208-
* <p>
209-
* The matching of each pattern is performed by a
210-
* {@link PathPatternMessageMatcher}. If no destination is found on the
211-
* {@code Message}, then each {@code Matcher} returns false.
212-
* @param patterns the patterns to create {@link PathPatternMessageMatcher} from.
213-
* @since 6.5
214-
*/
215-
public Builder.Constraint simpTypeMessageDestinationPatterns(String... patterns) {
216-
return destinationPathPatterns(SimpMessageType.MESSAGE, patterns);
217-
}
218-
219-
/**
220-
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that
221-
* match on {@code SimpMessageType.SUBSCRIBE}. If no destination is found on the
222-
* Message, then the Matcher returns false.
223-
* @param patterns the patterns to create
224-
* {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
225-
* from.
226-
* @deprecated use {@link #simpTypeSubscribeDestinationPatterns(String...)}
185+
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} (or
186+
* {@link PathPatternMessageMatcher} if the application has configured a
187+
* {@link org.springframework.security.messaging.util.matcher.PathPatternMessageMatcherBuilderFactoryBean})
188+
* instances that match on {@code SimpMessageType.SUBSCRIBE}. If no destination is
189+
* found on the Message, then the Matcher returns false.
190+
* @param patterns the patterns to create {@code MessageMatcher}s from.
227191
*/
228-
@Deprecated
229192
public Builder.Constraint simpSubscribeDestMatchers(String... patterns) {
230193
return simpDestMatchers(SimpMessageType.SUBSCRIBE, patterns);
231194
}
232195

233196
/**
234-
* Allows the creation of a security {@link Constraint} applying to messages of
235-
* the type {@code SimpMessageType.SUBSCRIBE} whose destinations match the
236-
* provided {@code patterns}.
237-
* <p>
238-
* The matching of each pattern is performed by a
239-
* {@link PathPatternMessageMatcher}. If no destination is found on the
240-
* {@code Message}, then each {@code Matcher} returns false.
241-
* @param patterns the patterns to create {@link PathPatternMessageMatcher} from.
242-
* @since 6.5
243-
*/
244-
public Builder.Constraint simpTypeSubscribeDestinationPatterns(String... patterns) {
245-
return destinationPathPatterns(SimpMessageType.SUBSCRIBE, patterns);
246-
}
247-
248-
/**
249-
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances. If no
250-
* destination is found on the Message, then the Matcher returns false.
197+
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances, or
198+
* {@link PathPatternMessageMatcher} if the application has configured a
199+
* {@link org.springframework.security.messaging.util.matcher.PathPatternMessageMatcherBuilderFactoryBean}.
200+
* If no destination is found on the Message, then the Matcher returns false.
251201
* @param type the {@link SimpMessageType} to match on. If null, the
252202
* {@link SimpMessageType} is not considered for matching.
253-
* @param patterns the patterns to create
254-
* {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
255-
* from.
203+
* @param patterns the patterns to create {@code MessageMatcher}s from.
256204
* @return the {@link Builder.Constraint} that is associated to the
257205
* {@link MessageMatcher}
258-
* @deprecated use {@link #destinationPathPatterns(String...)}
259206
*/
260-
@Deprecated
261207
private Builder.Constraint simpDestMatchers(SimpMessageType type, String... patterns) {
262208
List<MessageMatcher<?>> matchers = new ArrayList<>(patterns.length);
263209
for (String pattern : patterns) {
264-
MessageMatcher<Object> matcher = new LazySimpDestinationMessageMatcher(pattern, type);
210+
MessageMatcher<Object> matcher = MessageMatcherFactory.usesPathPatterns()
211+
? MessageMatcherFactory.matcher(pattern, type)
212+
: new LazySimpDestinationMessageMatcher(pattern, type);
265213
matchers.add(matcher);
266214
}
267215
return new Builder.Constraint(matchers);
268216
}
269217

270-
/**
271-
* Allows the creation of a security {@link Constraint} applying to messages of
272-
* the provided {@code type} whose destinations match the provided
273-
* {@code patterns}.
274-
* <p>
275-
* The matching of each pattern is performed by a
276-
* {@link PathPatternMessageMatcher}. If no destination is found on the
277-
* {@code Message}, then each {@code Matcher} returns false.
278-
* </p>
279-
* @param type the {@link SimpMessageType} to match on. If null, the
280-
* {@link SimpMessageType} is not considered for matching.
281-
* @param patterns the patterns to create {@link PathPatternMessageMatcher} from.
282-
* @return the {@link Builder.Constraint} that is associated to the
283-
* {@link MessageMatcher}s
284-
* @since 6.5
285-
*/
286-
private Builder.Constraint destinationPathPatterns(SimpMessageType type, String... patterns) {
287-
List<MessageMatcher<?>> matchers = new ArrayList<>(patterns.length);
288-
for (String pattern : patterns) {
289-
MessageMatcher<Object> matcher = new LazySimpDestinationPatternMessageMatcher(pattern, type,
290-
this.useHttpPathSeparator);
291-
matchers.add(matcher);
292-
}
293-
return new Builder.Constraint(matchers);
294-
}
295-
296-
/**
297-
* Instruct this builder to match message destinations using the separator
298-
* configured in
299-
* {@link org.springframework.http.server.PathContainer.Options#MESSAGE_ROUTE}
300-
*/
301-
public Builder messageRouteSeparator() {
302-
this.useHttpPathSeparator = false;
303-
return this;
304-
}
305-
306218
/**
307219
* The {@link PathMatcher} to be used with the
308220
* {@link Builder#simpDestMatchers(String...)}. The default is to use the default
309221
* constructor of {@link AntPathMatcher}.
310222
* @param pathMatcher the {@link PathMatcher} to use. Cannot be null.
311223
* @return the {@link Builder} for further customization.
312-
* @deprecated use {@link #messageRouteSeparator()} to alter the path separator
224+
* @deprecated
313225
*/
314226
@Deprecated
315227
public Builder simpDestPathMatcher(PathMatcher pathMatcher) {
@@ -324,7 +236,7 @@ public Builder simpDestPathMatcher(PathMatcher pathMatcher) {
324236
* computation or lookup of the {@link PathMatcher}.
325237
* @param pathMatcher the {@link PathMatcher} to use. Cannot be null.
326238
* @return the {@link Builder} for further customization.
327-
* @deprecated use {@link #messageRouteSeparator()} to alter the path separator
239+
* @deprecated
328240
*/
329241
@Deprecated
330242
public Builder simpDestPathMatcher(Supplier<PathMatcher> pathMatcher) {
@@ -513,40 +425,6 @@ Map<String, String> extractPathVariables(Message<?> message) {
513425

514426
}
515427

516-
private static final class LazySimpDestinationPatternMessageMatcher implements MessageMatcher<Object> {
517-
518-
private final Supplier<PathPatternMessageMatcher> delegate;
519-
520-
private LazySimpDestinationPatternMessageMatcher(String pattern, SimpMessageType type,
521-
boolean useHttpPathSeparator) {
522-
this.delegate = SingletonSupplier.of(() -> {
523-
PathPatternParser dotSeparatedPathParser = new PathPatternParser();
524-
dotSeparatedPathParser.setPathOptions(PathContainer.Options.MESSAGE_ROUTE);
525-
PathPatternMessageMatcher.Builder builder = (useHttpPathSeparator)
526-
? PathPatternMessageMatcher.withDefaults()
527-
: PathPatternMessageMatcher.withPathPatternParser(dotSeparatedPathParser);
528-
if (type == null) {
529-
return builder.matcher(pattern);
530-
}
531-
if (SimpMessageType.MESSAGE == type || SimpMessageType.SUBSCRIBE == type) {
532-
return builder.matcher(pattern, type);
533-
}
534-
throw new IllegalStateException(type + " is not supported since it does not have a destination");
535-
});
536-
}
537-
538-
@Override
539-
public boolean matches(Message<?> message) {
540-
return this.delegate.get().matches(message);
541-
}
542-
543-
Map<String, String> extractPathVariables(Message<?> message) {
544-
MatchResult matchResult = this.delegate.get().matcher(message);
545-
return matchResult.getVariables();
546-
}
547-
548-
}
549-
550428
}
551429

552430
private static final class Entry<T> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2002-2025 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.messaging.util.matcher;
18+
19+
import org.springframework.context.ApplicationContext;
20+
import org.springframework.messaging.simp.SimpMessageType;
21+
22+
@Deprecated(forRemoval = true)
23+
public final class MessageMatcherFactory {
24+
25+
private static PathPatternMessageMatcher.Builder builder;
26+
27+
public static void setApplicationContext(ApplicationContext context) {
28+
builder = context.getBeanProvider(PathPatternMessageMatcher.Builder.class).getIfUnique();
29+
}
30+
31+
public static boolean usesPathPatterns() {
32+
return builder != null;
33+
}
34+
35+
public static MessageMatcher<?> matcher(String destination) {
36+
return builder.matcher(destination);
37+
}
38+
39+
public static MessageMatcher<Object> matcher(String destination, SimpMessageType type) {
40+
return (type != null) ? builder.matcher(destination, type) : builder.matcher(destination);
41+
}
42+
43+
private MessageMatcherFactory() {
44+
}
45+
46+
}

0 commit comments

Comments
 (0)