11/* 
2-  * Copyright 2002-2022  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. 
1717package  org .springframework .security .messaging .access .intercept ;
1818
1919import  java .util .ArrayList ;
20+ import  java .util .Arrays ;
2021import  java .util .List ;
2122import  java .util .Map ;
2223import  java .util .function .Supplier ;
3233import  org .springframework .security .authorization .AuthorizationDecision ;
3334import  org .springframework .security .authorization .AuthorizationManager ;
3435import  org .springframework .security .core .Authentication ;
36+ import  org .springframework .security .messaging .util .matcher .DestinationPathPatternMessageMatcher ;
3537import  org .springframework .security .messaging .util .matcher .MessageMatcher ;
3638import  org .springframework .security .messaging .util .matcher .SimpDestinationMessageMatcher ;
3739import  org .springframework .security .messaging .util .matcher .SimpMessageTypeMatcher ;
@@ -87,12 +89,11 @@ private MessageAuthorizationContext<?> authorizationContext(MessageMatcher<?> ma
8789		if  (!matcher .matches ((Message ) message )) {
8890			return  null ;
8991		}
90- 		if  (matcher  instanceof  SimpDestinationMessageMatcher   simp ) {
91- 			return  new  MessageAuthorizationContext <>(message , simp .extractPathVariables (message ));
92+ 		if  (matcher  instanceof  Builder . LazySimpDestinationMessageMatcher   pathMatcher ) {
93+ 			return  new  MessageAuthorizationContext <>(message , pathMatcher .extractPathVariables (message ));
9294		}
93- 		if  (matcher  instanceof  Builder .LazySimpDestinationMessageMatcher ) {
94- 			Builder .LazySimpDestinationMessageMatcher  path  = (Builder .LazySimpDestinationMessageMatcher ) matcher ;
95- 			return  new  MessageAuthorizationContext <>(message , path .extractPathVariables (message ));
95+ 		if  (matcher  instanceof  Builder .LazySimpDestinationPatternMessageMatcher  pathMatcher ) {
96+ 			return  new  MessageAuthorizationContext <>(message , pathMatcher .extractPathVariables (message ));
9697		}
9798		return  new  MessageAuthorizationContext <>(message );
9899	}
@@ -112,8 +113,11 @@ public static final class Builder {
112113
113114		private  final  List <Entry <AuthorizationManager <MessageAuthorizationContext <?>>>> mappings  = new  ArrayList <>();
114115
116+ 		@ Deprecated 
115117		private  Supplier <PathMatcher > pathMatcher  = AntPathMatcher ::new ;
116118
119+ 		private  boolean  useHttpPathSeparator  = true ;
120+ 
117121		public  Builder () {
118122		}
119123
@@ -132,11 +136,11 @@ public Builder.Constraint anyMessage() {
132136		 * @return the Expression to associate 
133137		 */ 
134138		public  Builder .Constraint  nullDestMatcher () {
135- 			return  matchers (SimpDestinationMessageMatcher .NULL_DESTINATION_MATCHER );
139+ 			return  matchers (DestinationPathPatternMessageMatcher .NULL_DESTINATION_MATCHER );
136140		}
137141
138142		/** 
139- 		 * Maps a {@link List} of {@link SimpDestinationMessageMatcher } instances. 
143+ 		 * Maps a {@link List} of {@link SimpMessageTypeMatcher } instances. 
140144		 * @param typesToMatch the {@link SimpMessageType} instance to match on 
141145		 * @return the {@link Builder.Constraint} associated to the matchers. 
142146		 */ 
@@ -156,35 +160,90 @@ public Builder.Constraint simpTypeMatchers(SimpMessageType... typesToMatch) {
156160		 * @param patterns the patterns to create 
157161		 * {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher} 
158162		 * from. 
163+ 		 * @deprecated use {@link #destinationPathPatterns(String...)} 
159164		 */ 
165+ 		@ Deprecated 
160166		public  Builder .Constraint  simpDestMatchers (String ... patterns ) {
161167			return  simpDestMatchers (null , patterns );
162168		}
163169
170+ 		/** 
171+ 		 * Allows the creation of a security {@link Constraint} applying to messages whose 
172+ 		 * destinations match the provided {@code patterns}. 
173+ 		 * <p> 
174+ 		 * 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. 
178+ 		 * </p> 
179+ 		 * @param patterns the destination path patterns to which the security 
180+ 		 * {@code Constraint} will be applicable 
181+ 		 * @since 6.5 
182+ 		 */ 
183+ 		public  Builder .Constraint  destinationPathPatterns (String ... patterns ) {
184+ 			return  destinationPathPatterns (null , patterns );
185+ 		}
186+ 
164187		/** 
165188		 * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that 
166189		 * match on {@code SimpMessageType.MESSAGE}. If no destination is found on the 
167190		 * Message, then the Matcher returns false. 
168191		 * @param patterns the patterns to create 
169192		 * {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher} 
170193		 * from. 
194+ 		 * @deprecated use {@link #destinationPathPatterns(String...)} 
171195		 */ 
196+ 		@ Deprecated 
172197		public  Builder .Constraint  simpMessageDestMatchers (String ... patterns ) {
173198			return  simpDestMatchers (SimpMessageType .MESSAGE , patterns );
174199		}
175200
201+ 		/** 
202+ 		 * Allows the creation of a security {@link Constraint} applying to messages of 
203+ 		 * the type {@code SimpMessageType.MESSAGE} whose destinations match the provided 
204+ 		 * {@code patterns}. 
205+ 		 * <p> 
206+ 		 * The matching of each pattern is performed by a 
207+ 		 * {@link DestinationPathPatternMessageMatcher}. If no destination is found on the 
208+ 		 * {@code Message}, then each {@code Matcher} returns false. 
209+ 		 * @param patterns the patterns to create 
210+ 		 * {@link DestinationPathPatternMessageMatcher} from. 
211+ 		 * @since 6.5 
212+ 		 */ 
213+ 		public  Builder .Constraint  simpTypeMessageDestinationPatterns (String ... patterns ) {
214+ 			return  destinationPathPatterns (SimpMessageType .MESSAGE , patterns );
215+ 		}
216+ 
176217		/** 
177218		 * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that 
178219		 * match on {@code SimpMessageType.SUBSCRIBE}. If no destination is found on the 
179220		 * Message, then the Matcher returns false. 
180221		 * @param patterns the patterns to create 
181222		 * {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher} 
182223		 * from. 
224+ 		 * @deprecated use {@link #simpTypeSubscribeDestinationPatterns(String...)} 
183225		 */ 
226+ 		@ Deprecated 
184227		public  Builder .Constraint  simpSubscribeDestMatchers (String ... patterns ) {
185228			return  simpDestMatchers (SimpMessageType .SUBSCRIBE , patterns );
186229		}
187230
231+ 		/** 
232+ 		 * Allows the creation of a security {@link Constraint} applying to messages of 
233+ 		 * the type {@code SimpMessageType.SUBSCRIBE} whose destinations match the 
234+ 		 * provided {@code patterns}. 
235+ 		 * <p> 
236+ 		 * The matching of each pattern is performed by a 
237+ 		 * {@link DestinationPathPatternMessageMatcher}. If no destination is found on the 
238+ 		 * {@code Message}, then each {@code Matcher} returns false. 
239+ 		 * @param patterns the patterns to create 
240+ 		 * {@link DestinationPathPatternMessageMatcher} from. 
241+ 		 * @since 6.5 
242+ 		 */ 
243+ 		public  Builder .Constraint  simpTypeSubscribeDestinationPatterns (String ... patterns ) {
244+ 			return  destinationPathPatterns (SimpMessageType .SUBSCRIBE , patterns );
245+ 		}
246+ 
188247		/** 
189248		 * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances. If no 
190249		 * destination is found on the Message, then the Matcher returns false. 
@@ -195,7 +254,9 @@ public Builder.Constraint simpSubscribeDestMatchers(String... patterns) {
195254		 * from. 
196255		 * @return the {@link Builder.Constraint} that is associated to the 
197256		 * {@link MessageMatcher} 
257+ 		 * @deprecated use {@link #destinationPathPatterns(String...)} 
198258		 */ 
259+ 		@ Deprecated 
199260		private  Builder .Constraint  simpDestMatchers (SimpMessageType  type , String ... patterns ) {
200261			List <MessageMatcher <?>> matchers  = new  ArrayList <>(patterns .length );
201262			for  (String  pattern  : patterns ) {
@@ -205,13 +266,52 @@ private Builder.Constraint simpDestMatchers(SimpMessageType type, String... patt
205266			return  new  Builder .Constraint (matchers );
206267		}
207268
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 DestinationPathPatternMessageMatcher}. 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 
281+ 		 * {@link DestinationPathPatternMessageMatcher} 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+ 
208306		/** 
209307		 * The {@link PathMatcher} to be used with the 
210308		 * {@link Builder#simpDestMatchers(String...)}. The default is to use the default 
211309		 * constructor of {@link AntPathMatcher}. 
212310		 * @param pathMatcher the {@link PathMatcher} to use. Cannot be null. 
213311		 * @return the {@link Builder} for further customization. 
312+ 		 * @deprecated use {@link #messageRouteSeparator()} to alter the path separator 
214313		 */ 
314+ 		@ Deprecated 
215315		public  Builder  simpDestPathMatcher (PathMatcher  pathMatcher ) {
216316			Assert .notNull (pathMatcher , "pathMatcher cannot be null" );
217317			this .pathMatcher  = () -> pathMatcher ;
@@ -224,7 +324,9 @@ public Builder simpDestPathMatcher(PathMatcher pathMatcher) {
224324		 * computation or lookup of the {@link PathMatcher}. 
225325		 * @param pathMatcher the {@link PathMatcher} to use. Cannot be null. 
226326		 * @return the {@link Builder} for further customization. 
327+ 		 * @deprecated use {@link #messageRouteSeparator()} to alter the path separator 
227328		 */ 
329+ 		@ Deprecated 
228330		public  Builder  simpDestPathMatcher (Supplier <PathMatcher > pathMatcher ) {
229331			Assert .notNull (pathMatcher , "pathMatcher cannot be null" );
230332			this .pathMatcher  = pathMatcher ;
@@ -240,9 +342,7 @@ public Builder simpDestPathMatcher(Supplier<PathMatcher> pathMatcher) {
240342		 */ 
241343		public  Builder .Constraint  matchers (MessageMatcher <?>... matchers ) {
242344			List <MessageMatcher <?>> builders  = new  ArrayList <>(matchers .length );
243- 			for  (MessageMatcher <?> matcher  : matchers ) {
244- 				builders .add (matcher );
245- 			}
345+ 			builders .addAll (Arrays .asList (matchers ));
246346			return  new  Builder .Constraint (builders );
247347		}
248348
@@ -381,6 +481,7 @@ public Builder access(AuthorizationManager<MessageAuthorizationContext<?>> autho
381481
382482		}
383483
484+ 		@ Deprecated 
384485		private  final  class  LazySimpDestinationMessageMatcher  implements  MessageMatcher <Object > {
385486
386487			private  final  Supplier <SimpDestinationMessageMatcher > delegate ;
@@ -412,6 +513,37 @@ Map<String, String> extractPathVariables(Message<?> message) {
412513
413514		}
414515
516+ 		private  static  final  class  LazySimpDestinationPatternMessageMatcher  implements  MessageMatcher <Object > {
517+ 
518+ 			private  final  Supplier <DestinationPathPatternMessageMatcher > delegate ;
519+ 
520+ 			private  LazySimpDestinationPatternMessageMatcher (String  pattern , SimpMessageType  type ,
521+ 					boolean  useHttpPathSeparator ) {
522+ 				this .delegate  = SingletonSupplier .of (() -> {
523+ 					DestinationPathPatternMessageMatcher .Builder  builder  = (useHttpPathSeparator )
524+ 							? DestinationPathPatternMessageMatcher .withDefaults ()
525+ 							: DestinationPathPatternMessageMatcher .messageRoute ();
526+ 					if  (type  == null ) {
527+ 						return  builder .matcher (pattern );
528+ 					}
529+ 					if  (SimpMessageType .MESSAGE  == type  || SimpMessageType .SUBSCRIBE  == type ) {
530+ 						return  builder .messageType (type ).matcher (pattern );
531+ 					}
532+ 					throw  new  IllegalStateException (type  + " is not supported since it does not have a destination" );
533+ 				});
534+ 			}
535+ 
536+ 			@ Override 
537+ 			public  boolean  matches (Message <?> message ) {
538+ 				return  this .delegate .get ().matches (message );
539+ 			}
540+ 
541+ 			Map <String , String > extractPathVariables (Message <?> message ) {
542+ 				return  this .delegate .get ().extractPathVariables (message );
543+ 			}
544+ 
545+ 		}
546+ 
415547	}
416548
417549	private  static  final  class  Entry <T > {
@@ -420,7 +552,7 @@ private static final class Entry<T> {
420552
421553		private  final  T  entry ;
422554
423- 		Entry (MessageMatcher  requestMatcher , T  entry ) {
555+ 		Entry (MessageMatcher <?>  requestMatcher , T  entry ) {
424556			this .messageMatcher  = requestMatcher ;
425557			this .entry  = entry ;
426558		}
0 commit comments