Skip to content

Commit 48183da

Browse files
committed
Refactor PathPatternMessageMatcher
Signed-off-by: Pat McCusker <[email protected]>
1 parent 51b701d commit 48183da

File tree

7 files changed

+197
-113
lines changed

7 files changed

+197
-113
lines changed

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

Lines changed: 1 addition & 1 deletion
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-2024 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

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,23 @@
2626
import org.apache.commons.logging.LogFactory;
2727

2828
import org.springframework.core.log.LogMessage;
29+
import org.springframework.http.server.PathContainer;
2930
import org.springframework.messaging.Message;
3031
import org.springframework.messaging.simp.SimpMessageType;
3132
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
3233
import org.springframework.security.authorization.AuthorityAuthorizationManager;
3334
import org.springframework.security.authorization.AuthorizationDecision;
3435
import org.springframework.security.authorization.AuthorizationManager;
3536
import org.springframework.security.core.Authentication;
36-
import org.springframework.security.messaging.util.matcher.DestinationPathPatternMessageMatcher;
3737
import org.springframework.security.messaging.util.matcher.MessageMatcher;
38+
import org.springframework.security.messaging.util.matcher.PathPatternMessageMatcher;
3839
import org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher;
3940
import org.springframework.security.messaging.util.matcher.SimpMessageTypeMatcher;
4041
import org.springframework.util.AntPathMatcher;
4142
import org.springframework.util.Assert;
4243
import org.springframework.util.PathMatcher;
4344
import org.springframework.util.function.SingletonSupplier;
45+
import org.springframework.web.util.pattern.PathPatternParser;
4446

4547
public final class MessageMatcherDelegatingAuthorizationManager implements AuthorizationManager<Message<?>> {
4648

@@ -136,7 +138,7 @@ public Builder.Constraint anyMessage() {
136138
* @return the Expression to associate
137139
*/
138140
public Builder.Constraint nullDestMatcher() {
139-
return matchers(DestinationPathPatternMessageMatcher.NULL_DESTINATION_MATCHER);
141+
return matchers(PathPatternMessageMatcher.NULL_DESTINATION_MATCHER);
140142
}
141143

142144
/**
@@ -172,9 +174,9 @@ public Builder.Constraint simpDestMatchers(String... patterns) {
172174
* destinations match the provided {@code patterns}.
173175
* <p>
174176
* The matching of each pattern is performed by a
175-
* {@link DestinationPathPatternMessageMatcher} instance that matches
176-
* irrespectively of {@link SimpMessageType}. If no destination is found on the
177-
* {@code Message}, then each {@code Matcher} returns false.
177+
* {@link PathPatternMessageMatcher} instance that matches irrespectively of
178+
* {@link SimpMessageType}. If no destination is found on the {@code Message},
179+
* then each {@code Matcher} returns false.
178180
* </p>
179181
* @param patterns the destination path patterns to which the security
180182
* {@code Constraint} will be applicable
@@ -204,10 +206,9 @@ public Builder.Constraint simpMessageDestMatchers(String... patterns) {
204206
* {@code patterns}.
205207
* <p>
206208
* The matching of each pattern is performed by a
207-
* {@link DestinationPathPatternMessageMatcher}. If no destination is found on the
209+
* {@link PathPatternMessageMatcher}. If no destination is found on the
208210
* {@code Message}, then each {@code Matcher} returns false.
209-
* @param patterns the patterns to create
210-
* {@link DestinationPathPatternMessageMatcher} from.
211+
* @param patterns the patterns to create {@link PathPatternMessageMatcher} from.
211212
* @since 6.5
212213
*/
213214
public Builder.Constraint simpTypeMessageDestinationPatterns(String... patterns) {
@@ -234,10 +235,9 @@ public Builder.Constraint simpSubscribeDestMatchers(String... patterns) {
234235
* provided {@code patterns}.
235236
* <p>
236237
* The matching of each pattern is performed by a
237-
* {@link DestinationPathPatternMessageMatcher}. If no destination is found on the
238+
* {@link PathPatternMessageMatcher}. If no destination is found on the
238239
* {@code Message}, then each {@code Matcher} returns false.
239-
* @param patterns the patterns to create
240-
* {@link DestinationPathPatternMessageMatcher} from.
240+
* @param patterns the patterns to create {@link PathPatternMessageMatcher} from.
241241
* @since 6.5
242242
*/
243243
public Builder.Constraint simpTypeSubscribeDestinationPatterns(String... patterns) {
@@ -272,13 +272,12 @@ private Builder.Constraint simpDestMatchers(SimpMessageType type, String... patt
272272
* {@code patterns}.
273273
* <p>
274274
* The matching of each pattern is performed by a
275-
* {@link DestinationPathPatternMessageMatcher}. If no destination is found on the
275+
* {@link PathPatternMessageMatcher}. If no destination is found on the
276276
* {@code Message}, then each {@code Matcher} returns false.
277277
* </p>
278278
* @param type the {@link SimpMessageType} to match on. If null, the
279279
* {@link SimpMessageType} is not considered for matching.
280-
* @param patterns the patterns to create
281-
* {@link DestinationPathPatternMessageMatcher} from.
280+
* @param patterns the patterns to create {@link PathPatternMessageMatcher} from.
282281
* @return the {@link Builder.Constraint} that is associated to the
283282
* {@link MessageMatcher}s
284283
* @since 6.5
@@ -515,19 +514,21 @@ Map<String, String> extractPathVariables(Message<?> message) {
515514

516515
private static final class LazySimpDestinationPatternMessageMatcher implements MessageMatcher<Object> {
517516

518-
private final Supplier<DestinationPathPatternMessageMatcher> delegate;
517+
private final Supplier<PathPatternMessageMatcher> delegate;
519518

520519
private LazySimpDestinationPatternMessageMatcher(String pattern, SimpMessageType type,
521520
boolean useHttpPathSeparator) {
522521
this.delegate = SingletonSupplier.of(() -> {
523-
DestinationPathPatternMessageMatcher.Builder builder = (useHttpPathSeparator)
524-
? DestinationPathPatternMessageMatcher.withDefaults()
525-
: DestinationPathPatternMessageMatcher.messageRoute();
522+
PathPatternParser dotSeparatedPathParser = new PathPatternParser();
523+
dotSeparatedPathParser.setPathOptions(PathContainer.Options.MESSAGE_ROUTE);
524+
PathPatternMessageMatcher.Builder builder = (useHttpPathSeparator)
525+
? PathPatternMessageMatcher.withDefaults()
526+
: PathPatternMessageMatcher.withPathPatternParser(dotSeparatedPathParser);
526527
if (type == null) {
527528
return builder.matcher(pattern);
528529
}
529530
if (SimpMessageType.MESSAGE == type || SimpMessageType.SUBSCRIBE == type) {
530-
return builder.messageType(type).matcher(pattern);
531+
return builder.matcher(pattern, type);
531532
}
532533
throw new IllegalStateException(type + " is not supported since it does not have a destination");
533534
});
@@ -539,7 +540,8 @@ public boolean matches(Message<?> message) {
539540
}
540541

541542
Map<String, String> extractPathVariables(Message<?> message) {
542-
return this.delegate.get().extractPathVariables(message);
543+
MatchResult matchResult = this.delegate.get().matcher(message);
544+
return matchResult.getVariables();
543545
}
544546

545547
}

messaging/src/main/java/org/springframework/security/messaging/util/matcher/MessageMatcher.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package org.springframework.security.messaging.util.matcher;
1818

19+
import java.util.Collections;
20+
import java.util.Map;
21+
1922
import org.springframework.messaging.Message;
2023

2124
/**
@@ -50,4 +53,80 @@ public String toString() {
5053
*/
5154
boolean matches(Message<? extends T> message);
5255

56+
/**
57+
* Returns a {@link MatchResult} for this {@code MessageMatcher}. The default
58+
* implementation returns {@link Collections#emptyMap()} when
59+
* {@link MatchResult#getVariables()} is invoked.
60+
* @return the {@code MatchResult} from comparing this {@code MessageMatcher} against
61+
* the {@code Message}
62+
* @since 6.5
63+
*/
64+
default MatchResult matcher(Message<? extends T> message) {
65+
boolean match = matches(message);
66+
return new MatchResult(match, Collections.emptyMap());
67+
}
68+
69+
/**
70+
* The result of matching against a {@code Message} contains the status, true or
71+
* false, of the match and if present, any variables extracted from the match
72+
*
73+
* @since 6.5
74+
*/
75+
class MatchResult {
76+
77+
private final boolean match;
78+
79+
private final Map<String, String> variables;
80+
81+
MatchResult(boolean match, Map<String, String> variables) {
82+
this.match = match;
83+
this.variables = variables;
84+
}
85+
86+
/**
87+
* @return true if the comparison against the {@code Message} produced a
88+
* successful match
89+
*/
90+
public boolean isMatch() {
91+
return this.match;
92+
}
93+
94+
/**
95+
* Returns the extracted variable values where the key is the variable name and
96+
* the value is the variable value
97+
* @return a map containing key-value pairs representing extracted variable names
98+
* and variable values
99+
*/
100+
public Map<String, String> getVariables() {
101+
return this.variables;
102+
}
103+
104+
/**
105+
* Creates an instance of {@link MatchResult} that is a match with no variables
106+
* @return
107+
*/
108+
public static MatchResult match() {
109+
return new MatchResult(true, Collections.emptyMap());
110+
}
111+
112+
/**
113+
* Creates an instance of {@link MatchResult} that is a match with the specified
114+
* variables
115+
* @param variables
116+
* @return
117+
*/
118+
public static MatchResult match(Map<String, String> variables) {
119+
return new MatchResult(true, variables);
120+
}
121+
122+
/**
123+
* Creates an instance of {@link MatchResult} that is not a match.
124+
* @return a {@code MatchResult} with match set to false
125+
*/
126+
public static MatchResult notMatch() {
127+
return new MatchResult(false, Collections.emptyMap());
128+
}
129+
130+
}
131+
53132
}

0 commit comments

Comments
 (0)