1
1
/*
2
- * Copyright 2002-2022 the original author or authors.
2
+ * Copyright 2002-2025 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
17
17
package org .springframework .security .messaging .access .intercept ;
18
18
19
19
import java .util .ArrayList ;
20
+ import java .util .Arrays ;
20
21
import java .util .List ;
21
22
import java .util .Map ;
22
23
import java .util .function .Supplier ;
25
26
import org .apache .commons .logging .LogFactory ;
26
27
27
28
import org .springframework .core .log .LogMessage ;
29
+ import org .springframework .http .server .PathContainer ;
28
30
import org .springframework .messaging .Message ;
29
31
import org .springframework .messaging .simp .SimpMessageType ;
30
32
import org .springframework .security .authorization .AuthenticatedAuthorizationManager ;
33
35
import org .springframework .security .authorization .AuthorizationManager ;
34
36
import org .springframework .security .core .Authentication ;
35
37
import org .springframework .security .messaging .util .matcher .MessageMatcher ;
38
+ import org .springframework .security .messaging .util .matcher .PathPatternMessageMatcher ;
36
39
import org .springframework .security .messaging .util .matcher .SimpDestinationMessageMatcher ;
37
40
import org .springframework .security .messaging .util .matcher .SimpMessageTypeMatcher ;
38
41
import org .springframework .util .AntPathMatcher ;
39
42
import org .springframework .util .Assert ;
40
43
import org .springframework .util .PathMatcher ;
41
44
import org .springframework .util .function .SingletonSupplier ;
45
+ import org .springframework .web .util .pattern .PathPatternParser ;
42
46
43
47
public final class MessageMatcherDelegatingAuthorizationManager implements AuthorizationManager <Message <?>> {
44
48
@@ -87,12 +91,11 @@ private MessageAuthorizationContext<?> authorizationContext(MessageMatcher<?> ma
87
91
if (!matcher .matches ((Message ) message )) {
88
92
return null ;
89
93
}
90
- if (matcher instanceof SimpDestinationMessageMatcher simp ) {
91
- return new MessageAuthorizationContext <>(message , simp .extractPathVariables (message ));
94
+ if (matcher instanceof Builder . LazySimpDestinationMessageMatcher pathMatcher ) {
95
+ return new MessageAuthorizationContext <>(message , pathMatcher .extractPathVariables (message ));
92
96
}
93
- if (matcher instanceof Builder .LazySimpDestinationMessageMatcher ) {
94
- Builder .LazySimpDestinationMessageMatcher path = (Builder .LazySimpDestinationMessageMatcher ) matcher ;
95
- return new MessageAuthorizationContext <>(message , path .extractPathVariables (message ));
97
+ if (matcher instanceof Builder .LazySimpDestinationPatternMessageMatcher pathMatcher ) {
98
+ return new MessageAuthorizationContext <>(message , pathMatcher .extractPathVariables (message ));
96
99
}
97
100
return new MessageAuthorizationContext <>(message );
98
101
}
@@ -112,8 +115,11 @@ public static final class Builder {
112
115
113
116
private final List <Entry <AuthorizationManager <MessageAuthorizationContext <?>>>> mappings = new ArrayList <>();
114
117
118
+ @ Deprecated
115
119
private Supplier <PathMatcher > pathMatcher = AntPathMatcher ::new ;
116
120
121
+ private boolean useHttpPathSeparator = true ;
122
+
117
123
public Builder () {
118
124
}
119
125
@@ -132,11 +138,11 @@ public Builder.Constraint anyMessage() {
132
138
* @return the Expression to associate
133
139
*/
134
140
public Builder .Constraint nullDestMatcher () {
135
- return matchers (SimpDestinationMessageMatcher .NULL_DESTINATION_MATCHER );
141
+ return matchers (PathPatternMessageMatcher .NULL_DESTINATION_MATCHER );
136
142
}
137
143
138
144
/**
139
- * Maps a {@link List} of {@link SimpDestinationMessageMatcher } instances.
145
+ * Maps a {@link List} of {@link SimpMessageTypeMatcher } instances.
140
146
* @param typesToMatch the {@link SimpMessageType} instance to match on
141
147
* @return the {@link Builder.Constraint} associated to the matchers.
142
148
*/
@@ -156,35 +162,88 @@ public Builder.Constraint simpTypeMatchers(SimpMessageType... typesToMatch) {
156
162
* @param patterns the patterns to create
157
163
* {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
158
164
* from.
165
+ * @deprecated use {@link #destinationPathPatterns(String...)}
159
166
*/
167
+ @ Deprecated
160
168
public Builder .Constraint simpDestMatchers (String ... patterns ) {
161
169
return simpDestMatchers (null , patterns );
162
170
}
163
171
172
+ /**
173
+ * Allows the creation of a security {@link Constraint} applying to messages whose
174
+ * destinations match the provided {@code patterns}.
175
+ * <p>
176
+ * The matching of each pattern is performed by a
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.
180
+ * </p>
181
+ * @param patterns the destination path patterns to which the security
182
+ * {@code Constraint} will be applicable
183
+ * @since 6.5
184
+ */
185
+ public Builder .Constraint destinationPathPatterns (String ... patterns ) {
186
+ return destinationPathPatterns (null , patterns );
187
+ }
188
+
164
189
/**
165
190
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that
166
191
* match on {@code SimpMessageType.MESSAGE}. If no destination is found on the
167
192
* Message, then the Matcher returns false.
168
193
* @param patterns the patterns to create
169
194
* {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
170
195
* from.
196
+ * @deprecated use {@link #destinationPathPatterns(String...)}
171
197
*/
198
+ @ Deprecated
172
199
public Builder .Constraint simpMessageDestMatchers (String ... patterns ) {
173
200
return simpDestMatchers (SimpMessageType .MESSAGE , patterns );
174
201
}
175
202
203
+ /**
204
+ * Allows the creation of a security {@link Constraint} applying to messages of
205
+ * the type {@code SimpMessageType.MESSAGE} whose destinations match the provided
206
+ * {@code patterns}.
207
+ * <p>
208
+ * The matching of each pattern is performed by a
209
+ * {@link PathPatternMessageMatcher}. If no destination is found on the
210
+ * {@code Message}, then each {@code Matcher} returns false.
211
+ * @param patterns the patterns to create {@link PathPatternMessageMatcher} from.
212
+ * @since 6.5
213
+ */
214
+ public Builder .Constraint simpTypeMessageDestinationPatterns (String ... patterns ) {
215
+ return destinationPathPatterns (SimpMessageType .MESSAGE , patterns );
216
+ }
217
+
176
218
/**
177
219
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that
178
220
* match on {@code SimpMessageType.SUBSCRIBE}. If no destination is found on the
179
221
* Message, then the Matcher returns false.
180
222
* @param patterns the patterns to create
181
223
* {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
182
224
* from.
225
+ * @deprecated use {@link #simpTypeSubscribeDestinationPatterns(String...)}
183
226
*/
227
+ @ Deprecated
184
228
public Builder .Constraint simpSubscribeDestMatchers (String ... patterns ) {
185
229
return simpDestMatchers (SimpMessageType .SUBSCRIBE , patterns );
186
230
}
187
231
232
+ /**
233
+ * Allows the creation of a security {@link Constraint} applying to messages of
234
+ * the type {@code SimpMessageType.SUBSCRIBE} whose destinations match the
235
+ * provided {@code patterns}.
236
+ * <p>
237
+ * The matching of each pattern is performed by a
238
+ * {@link PathPatternMessageMatcher}. If no destination is found on the
239
+ * {@code Message}, then each {@code Matcher} returns false.
240
+ * @param patterns the patterns to create {@link PathPatternMessageMatcher} from.
241
+ * @since 6.5
242
+ */
243
+ public Builder .Constraint simpTypeSubscribeDestinationPatterns (String ... patterns ) {
244
+ return destinationPathPatterns (SimpMessageType .SUBSCRIBE , patterns );
245
+ }
246
+
188
247
/**
189
248
* Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances. If no
190
249
* destination is found on the Message, then the Matcher returns false.
@@ -195,7 +254,9 @@ public Builder.Constraint simpSubscribeDestMatchers(String... patterns) {
195
254
* from.
196
255
* @return the {@link Builder.Constraint} that is associated to the
197
256
* {@link MessageMatcher}
257
+ * @deprecated use {@link #destinationPathPatterns(String...)}
198
258
*/
259
+ @ Deprecated
199
260
private Builder .Constraint simpDestMatchers (SimpMessageType type , String ... patterns ) {
200
261
List <MessageMatcher <?>> matchers = new ArrayList <>(patterns .length );
201
262
for (String pattern : patterns ) {
@@ -205,13 +266,51 @@ private Builder.Constraint simpDestMatchers(SimpMessageType type, String... patt
205
266
return new Builder .Constraint (matchers );
206
267
}
207
268
269
+ /**
270
+ * Allows the creation of a security {@link Constraint} applying to messages of
271
+ * the provided {@code type} whose destinations match the provided
272
+ * {@code patterns}.
273
+ * <p>
274
+ * The matching of each pattern is performed by a
275
+ * {@link PathPatternMessageMatcher}. If no destination is found on the
276
+ * {@code Message}, then each {@code Matcher} returns false.
277
+ * </p>
278
+ * @param type the {@link SimpMessageType} to match on. If null, the
279
+ * {@link SimpMessageType} is not considered for matching.
280
+ * @param patterns the patterns to create {@link PathPatternMessageMatcher} from.
281
+ * @return the {@link Builder.Constraint} that is associated to the
282
+ * {@link MessageMatcher}s
283
+ * @since 6.5
284
+ */
285
+ private Builder .Constraint destinationPathPatterns (SimpMessageType type , String ... patterns ) {
286
+ List <MessageMatcher <?>> matchers = new ArrayList <>(patterns .length );
287
+ for (String pattern : patterns ) {
288
+ MessageMatcher <Object > matcher = new LazySimpDestinationPatternMessageMatcher (pattern , type ,
289
+ this .useHttpPathSeparator );
290
+ matchers .add (matcher );
291
+ }
292
+ return new Builder .Constraint (matchers );
293
+ }
294
+
295
+ /**
296
+ * Instruct this builder to match message destinations using the separator
297
+ * configured in
298
+ * {@link org.springframework.http.server.PathContainer.Options#MESSAGE_ROUTE}
299
+ */
300
+ public Builder messageRouteSeparator () {
301
+ this .useHttpPathSeparator = false ;
302
+ return this ;
303
+ }
304
+
208
305
/**
209
306
* The {@link PathMatcher} to be used with the
210
307
* {@link Builder#simpDestMatchers(String...)}. The default is to use the default
211
308
* constructor of {@link AntPathMatcher}.
212
309
* @param pathMatcher the {@link PathMatcher} to use. Cannot be null.
213
310
* @return the {@link Builder} for further customization.
311
+ * @deprecated use {@link #messageRouteSeparator()} to alter the path separator
214
312
*/
313
+ @ Deprecated
215
314
public Builder simpDestPathMatcher (PathMatcher pathMatcher ) {
216
315
Assert .notNull (pathMatcher , "pathMatcher cannot be null" );
217
316
this .pathMatcher = () -> pathMatcher ;
@@ -224,7 +323,9 @@ public Builder simpDestPathMatcher(PathMatcher pathMatcher) {
224
323
* computation or lookup of the {@link PathMatcher}.
225
324
* @param pathMatcher the {@link PathMatcher} to use. Cannot be null.
226
325
* @return the {@link Builder} for further customization.
326
+ * @deprecated use {@link #messageRouteSeparator()} to alter the path separator
227
327
*/
328
+ @ Deprecated
228
329
public Builder simpDestPathMatcher (Supplier <PathMatcher > pathMatcher ) {
229
330
Assert .notNull (pathMatcher , "pathMatcher cannot be null" );
230
331
this .pathMatcher = pathMatcher ;
@@ -240,9 +341,7 @@ public Builder simpDestPathMatcher(Supplier<PathMatcher> pathMatcher) {
240
341
*/
241
342
public Builder .Constraint matchers (MessageMatcher <?>... matchers ) {
242
343
List <MessageMatcher <?>> builders = new ArrayList <>(matchers .length );
243
- for (MessageMatcher <?> matcher : matchers ) {
244
- builders .add (matcher );
245
- }
344
+ builders .addAll (Arrays .asList (matchers ));
246
345
return new Builder .Constraint (builders );
247
346
}
248
347
@@ -381,6 +480,7 @@ public Builder access(AuthorizationManager<MessageAuthorizationContext<?>> autho
381
480
382
481
}
383
482
483
+ @ Deprecated
384
484
private final class LazySimpDestinationMessageMatcher implements MessageMatcher <Object > {
385
485
386
486
private final Supplier <SimpDestinationMessageMatcher > delegate ;
@@ -412,6 +512,40 @@ Map<String, String> extractPathVariables(Message<?> message) {
412
512
413
513
}
414
514
515
+ private static final class LazySimpDestinationPatternMessageMatcher implements MessageMatcher <Object > {
516
+
517
+ private final Supplier <PathPatternMessageMatcher > delegate ;
518
+
519
+ private LazySimpDestinationPatternMessageMatcher (String pattern , SimpMessageType type ,
520
+ boolean useHttpPathSeparator ) {
521
+ this .delegate = SingletonSupplier .of (() -> {
522
+ PathPatternParser dotSeparatedPathParser = new PathPatternParser ();
523
+ dotSeparatedPathParser .setPathOptions (PathContainer .Options .MESSAGE_ROUTE );
524
+ PathPatternMessageMatcher .Builder builder = (useHttpPathSeparator )
525
+ ? PathPatternMessageMatcher .withDefaults ()
526
+ : PathPatternMessageMatcher .withPathPatternParser (dotSeparatedPathParser );
527
+ if (type == null ) {
528
+ return builder .matcher (pattern );
529
+ }
530
+ if (SimpMessageType .MESSAGE == type || SimpMessageType .SUBSCRIBE == type ) {
531
+ return builder .matcher (pattern , type );
532
+ }
533
+ throw new IllegalStateException (type + " is not supported since it does not have a destination" );
534
+ });
535
+ }
536
+
537
+ @ Override
538
+ public boolean matches (Message <?> message ) {
539
+ return this .delegate .get ().matches (message );
540
+ }
541
+
542
+ Map <String , String > extractPathVariables (Message <?> message ) {
543
+ MatchResult matchResult = this .delegate .get ().matcher (message );
544
+ return matchResult .getVariables ();
545
+ }
546
+
547
+ }
548
+
415
549
}
416
550
417
551
private static final class Entry <T > {
@@ -420,7 +554,7 @@ private static final class Entry<T> {
420
554
421
555
private final T entry ;
422
556
423
- Entry (MessageMatcher requestMatcher , T entry ) {
557
+ Entry (MessageMatcher <?> requestMatcher , T entry ) {
424
558
this .messageMatcher = requestMatcher ;
425
559
this .entry = entry ;
426
560
}
0 commit comments