Преглед на файлове

SEC-2827: Clean up MessageMatcher Ambiguities

Rob Winch преди 10 години
родител
ревизия
414f98bee0

+ 58 - 11
config/src/main/java/org/springframework/security/config/annotation/web/messaging/MessageSecurityMetadataSourceRegistry.java

@@ -57,13 +57,23 @@ public class MessageSecurityMetadataSourceRegistry {
         return matchers(MessageMatcher.ANY_MESSAGE);
     }
 
+    /**
+     * Maps any {@link Message} that has a null SimpMessageHeaderAccessor destination header (i.e. CONNECT,
+     * CONNECT_ACK, HEARTBEAT, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER)
+     *
+     * @return the Expression to associate
+     */
+    public Constraint nullDestMatcher() {
+        return matchers(SimpDestinationMessageMatcher.NULL_DESTINATION_MATCHER);
+    }
+
     /**
      * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances.
      *
      * @param typesToMatch the {@link SimpMessageType} instance to match on
      * @return the {@link Constraint} associated to the matchers.
      */
-    public Constraint typeMatchers(SimpMessageType... typesToMatch) {
+    public Constraint simpTypeMatchers(SimpMessageType... typesToMatch) {
         MessageMatcher<?>[] typeMatchers = new MessageMatcher<?>[typesToMatch.length];
         for (int i = 0; i < typesToMatch.length; i++) {
             SimpMessageType typeToMatch = typesToMatch[i];
@@ -81,15 +91,45 @@ public class MessageSecurityMetadataSourceRegistry {
      *            the patterns to create
      *            {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
      *            from. Uses
-     *            {@link MessageSecurityMetadataSourceRegistry#pathMatcher(PathMatcher)}
+     *            {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}
      *            .
      *
      * @return the {@link Constraint} that is associated to the
      *         {@link MessageMatcher}
-     * @see {@link MessageSecurityMetadataSourceRegistry#pathMatcher(PathMatcher)}
+     * @see {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}
      */
-    public Constraint antMatchers(String... patterns) {
-        return antMatchers(null, patterns);
+    public Constraint simpDestMatchers(String... patterns) {
+        return simpDestMatchers(null, patterns);
+    }
+
+    /**
+     * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that match on {@code SimpMessageType.MESSAGE}.
+     * If no destination is found on the Message, then the Matcher returns
+     * false.
+     *
+     * @param patterns the patterns to create {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
+     *                    from. Uses {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}.
+     *
+     * @return the {@link Constraint}  that is associated to the {@link MessageMatcher}
+     * @see {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}
+     */
+    public Constraint simpDestMessageMatchers(String... patterns) {
+        return simpDestMatchers(SimpMessageType.MESSAGE, patterns);
+    }
+
+    /**
+     * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that match on {@code SimpMessageType.SUBSCRIBE}.
+     * If no destination is found on the Message, then the Matcher returns
+     * false.
+     *
+     * @param patterns the patterns to create {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
+     *                    from. Uses {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}.
+     *
+     * @return the {@link Constraint}  that is associated to the {@link MessageMatcher}
+     * @see {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}
+     */
+    public Constraint simpDestSubscribeMatchers(String... patterns) {
+        return simpDestMatchers(SimpMessageType.SUBSCRIBE, patterns);
     }
 
     /**
@@ -99,12 +139,12 @@ public class MessageSecurityMetadataSourceRegistry {
      *
      * @param type the {@link SimpMessageType} to match on. If null, the {@link SimpMessageType} is not considered for matching.
      * @param patterns the patterns to create {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
-     *                    from. Uses {@link MessageSecurityMetadataSourceRegistry#pathMatcher(PathMatcher)}.
+     *                    from. Uses {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}.
      *
      * @return the {@link Constraint}  that is associated to the {@link MessageMatcher}
-     * @see {@link MessageSecurityMetadataSourceRegistry#pathMatcher(PathMatcher)}
+     * @see {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}
      */
-    public Constraint antMatchers(SimpMessageType type, String... patterns) {
+    private Constraint simpDestMatchers(SimpMessageType type, String... patterns) {
         List<MatcherBuilder> matchers = new ArrayList<MatcherBuilder>(patterns.length);
         for(String pattern : patterns) {
             matchers.add(new PathMatcherMessageMatcherBuilder(pattern, type));
@@ -113,13 +153,13 @@ public class MessageSecurityMetadataSourceRegistry {
     }
 
     /**
-     * The {@link PathMatcher} to be used with the {@link MessageSecurityMetadataSourceRegistry#antMatchers(String...)}.
+     * The {@link PathMatcher} to be used with the {@link MessageSecurityMetadataSourceRegistry#simpDestMatchers(String...)}.
      * The default is to use the default constructor of {@link AntPathMatcher}.
      *
      * @param pathMatcher the {@link PathMatcher} to use. Cannot be null.
      * @return the {@link MessageSecurityMetadataSourceRegistry} for further customization.
      */
-    public MessageSecurityMetadataSourceRegistry pathMatcher(PathMatcher pathMatcher) {
+    public MessageSecurityMetadataSourceRegistry simpDestPathMatcher(PathMatcher pathMatcher) {
         Assert.notNull(pathMatcher, "pathMatcher cannot be null");
         this.pathMatcher = pathMatcher;
         return this;
@@ -344,7 +384,14 @@ public class MessageSecurityMetadataSourceRegistry {
         }
 
         public MessageMatcher<?> build() {
-            return new SimpDestinationMessageMatcher(pattern, type, pathMatcher);
+            if(type == null) {
+                return new SimpDestinationMessageMatcher(pattern, pathMatcher);
+            } else if(SimpMessageType.MESSAGE == type) {
+                return SimpDestinationMessageMatcher.createMessageMatcher(pattern, pathMatcher);
+            } else if(SimpMessageType.SUBSCRIBE == type) {
+                return SimpDestinationMessageMatcher.createSubscribeMatcher(pattern, pathMatcher);
+            }
+            throw new IllegalStateException(type + " is not supported since it does not have a destination");
         }
     }
 

+ 137 - 35
config/src/test/java/org/springframework/security/config/annotation/web/messaging/MessageSecurityMetadataSourceRegistryTests.java

@@ -22,6 +22,7 @@ import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 import org.springframework.messaging.Message;
 import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
+import org.springframework.messaging.simp.SimpMessageType;
 import org.springframework.messaging.support.MessageBuilder;
 import org.springframework.security.access.ConfigAttribute;
 import org.springframework.security.messaging.access.intercept.MessageSecurityMetadataSource;
@@ -48,20 +49,21 @@ public class MessageSecurityMetadataSourceRegistryTests {
         message = MessageBuilder
                 .withPayload("Hi")
                 .setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, "location")
-                   .build();
+                .setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.MESSAGE)
+                .build();
     }
 
     // See https://github.com/spring-projects/spring-security/commit/3f30529039c76facf335d6ca69d18d8ae287f3f9#commitcomment-7412712
     // https://jira.spring.io/browse/SPR-11660
     @Test
-    public void destinationMatcherCustom() {
+    public void simpDestMatchersCustom() {
         message = MessageBuilder
                 .withPayload("Hi")
                 .setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, "price.stock.1.2")
                 .build();
         messages
-                .pathMatcher(new AntPathMatcher("."))
-                .antMatchers("price.stock.*").permitAll();
+                .simpDestPathMatcher(new AntPathMatcher("."))
+                .simpDestMatchers("price.stock.*").permitAll();
 
         assertThat(getAttribute()).isNull();
 
@@ -70,21 +72,21 @@ public class MessageSecurityMetadataSourceRegistryTests {
                 .setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, "price.stock.1.2")
                 .build();
         messages
-                .pathMatcher(new AntPathMatcher("."))
-                .antMatchers("price.stock.**").permitAll();
+                .simpDestPathMatcher(new AntPathMatcher("."))
+                .simpDestMatchers("price.stock.**").permitAll();
 
         assertThat(getAttribute()).isEqualTo("permitAll");
     }
 
     @Test
-    public void destinationMatcherCustomSetAfterMatchersDoesNotMatter() {
+    public void simpDestMatchersCustomSetAfterMatchersDoesNotMatter() {
         message = MessageBuilder
                 .withPayload("Hi")
                 .setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, "price.stock.1.2")
                 .build();
         messages
-                .antMatchers("price.stock.*").permitAll()
-                .pathMatcher(new AntPathMatcher("."));
+                .simpDestMatchers("price.stock.*").permitAll()
+                .simpDestPathMatcher(new AntPathMatcher("."));
 
         assertThat(getAttribute()).isNull();
 
@@ -93,14 +95,15 @@ public class MessageSecurityMetadataSourceRegistryTests {
                 .setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, "price.stock.1.2")
                 .build();
         messages
-                .antMatchers("price.stock.**").permitAll()
-                .pathMatcher(new AntPathMatcher("."));
+                .simpDestMatchers("price.stock.**").permitAll()
+                .simpDestPathMatcher(new AntPathMatcher("."));
 
         assertThat(getAttribute()).isEqualTo("permitAll");
     }
+
     @Test(expected = IllegalArgumentException.class)
     public void pathMatcherNull() {
-        messages.pathMatcher(null);
+        messages.simpDestPathMatcher(null);
     }
 
     @Test
@@ -121,104 +124,203 @@ public class MessageSecurityMetadataSourceRegistryTests {
     }
 
     @Test
-    public void destinationMatcherExact() {
+    public void simpDestMatchersExact() {
         messages
-                .antMatchers("location").permitAll();
+                .simpDestMatchers("location").permitAll();
 
         assertThat(getAttribute()).isEqualTo("permitAll");
     }
 
     @Test
-    public void destinationMatcherMulti() {
+    public void simpDestMatchersMulti() {
         messages
-                .antMatchers("admin/**","api/**").hasRole("ADMIN")
-                .antMatchers("location").permitAll();
+                .simpDestMatchers("admin/**","api/**").hasRole("ADMIN")
+                .simpDestMatchers("location").permitAll();
 
         assertThat(getAttribute()).isEqualTo("permitAll");
     }
 
     @Test
-    public void destinationMatcherRole() {
+    public void simpDestMatchersRole() {
         messages
-                .antMatchers("admin/**","location/**").hasRole("ADMIN")
+                .simpDestMatchers("admin/**","location/**").hasRole("ADMIN")
                 .anyMessage().denyAll();
 
         assertThat(getAttribute()).isEqualTo("hasRole('ROLE_ADMIN')");
     }
 
     @Test
-    public void destinationMatcherAnyRole() {
+    public void simpDestMatchersAnyRole() {
         messages
-                .antMatchers("admin/**","location/**").hasAnyRole("ADMIN", "ROOT")
+                .simpDestMatchers("admin/**","location/**").hasAnyRole("ADMIN", "ROOT")
                 .anyMessage().denyAll();
 
         assertThat(getAttribute()).isEqualTo("hasAnyRole('ROLE_ADMIN','ROLE_ROOT')");
     }
 
     @Test
-    public void destinationMatcherAuthority() {
+    public void simpDestMatchersAuthority() {
         messages
-                .antMatchers("admin/**","location/**").hasAuthority("ROLE_ADMIN")
+                .simpDestMatchers("admin/**","location/**").hasAuthority("ROLE_ADMIN")
                 .anyMessage().fullyAuthenticated();
 
         assertThat(getAttribute()).isEqualTo("hasAuthority('ROLE_ADMIN')");
     }
 
     @Test
-    public void destinationMatcherAccess() {
+    public void simpDestMatchersAccess() {
         String expected = "hasRole('ROLE_ADMIN') and fullyAuthenticated";
         messages
-                .antMatchers("admin/**","location/**").access(expected)
+                .simpDestMatchers("admin/**","location/**").access(expected)
                 .anyMessage().denyAll();
 
         assertThat(getAttribute()).isEqualTo(expected);
     }
 
     @Test
-    public void destinationMatcherAnyAuthority() {
+    public void simpDestMatchersAnyAuthority() {
         messages
-                .antMatchers("admin/**","location/**").hasAnyAuthority("ROLE_ADMIN", "ROLE_ROOT")
+                .simpDestMatchers("admin/**","location/**").hasAnyAuthority("ROLE_ADMIN", "ROLE_ROOT")
                 .anyMessage().denyAll();
 
         assertThat(getAttribute()).isEqualTo("hasAnyAuthority('ROLE_ADMIN','ROLE_ROOT')");
     }
 
     @Test
-    public void destinationMatcherRememberMe() {
+    public void simpDestMatchersRememberMe() {
         messages
-                .antMatchers("admin/**","location/**").rememberMe()
+                .simpDestMatchers("admin/**","location/**").rememberMe()
                 .anyMessage().denyAll();
 
         assertThat(getAttribute()).isEqualTo("rememberMe");
     }
 
     @Test
-    public void destinationMatcherAnonymous() {
+    public void simpDestMatchersAnonymous() {
         messages
-                .antMatchers("admin/**","location/**").anonymous()
+                .simpDestMatchers("admin/**","location/**").anonymous()
                 .anyMessage().denyAll();
 
         assertThat(getAttribute()).isEqualTo("anonymous");
     }
 
     @Test
-    public void destinationMatcherFullyAuthenticated() {
+    public void simpDestMatchersFullyAuthenticated() {
         messages
-                .antMatchers("admin/**","location/**").fullyAuthenticated()
+                .simpDestMatchers("admin/**","location/**").fullyAuthenticated()
                 .anyMessage().denyAll();
 
         assertThat(getAttribute()).isEqualTo("fullyAuthenticated");
     }
 
     @Test
-    public void destinationMatcherDenyAll() {
+    public void simpDestMatchersDenyAll() {
         messages
-                .antMatchers("admin/**","location/**").denyAll()
+                .simpDestMatchers("admin/**","location/**").denyAll()
                 .anyMessage().permitAll();
 
         assertThat(getAttribute()).isEqualTo("denyAll");
     }
 
+    @Test
+    public void simpDestMessageMatchersNotMatch() {
+        messages
+                .simpDestMessageMatchers("admin/**").denyAll()
+                .anyMessage().permitAll();
+
+        assertThat(getAttribute()).isEqualTo("permitAll");
+    }
+
+    @Test
+    public void simpDestMessageMatchersMatch() {
+        messages
+                .simpDestMessageMatchers("location/**").denyAll()
+                .anyMessage().permitAll();
+
+        assertThat(getAttribute()).isEqualTo("denyAll");
+    }
+
+    @Test
+    public void simpDestSubscribeMatchersNotMatch() {
+        messages
+                .simpDestSubscribeMatchers("location/**").denyAll()
+                .anyMessage().permitAll();
+
+        assertThat(getAttribute()).isEqualTo("permitAll");
+    }
+
+    @Test
+    public void simpDestSubscribeMatchersMatch() {
+        message = MessageBuilder.fromMessage(message)
+                    .setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.SUBSCRIBE)
+                    .build();
+
+        messages
+                .simpDestSubscribeMatchers("location/**").denyAll()
+                .anyMessage().permitAll();
+
+        assertThat(getAttribute()).isEqualTo("denyAll");
+    }
+
+    @Test
+    public void nullDestMatcherNotMatches() {
+         messages
+             .nullDestMatcher().denyAll()
+             .anyMessage().permitAll();
+
+        assertThat(getAttribute()).isEqualTo("permitAll");
+    }
+
+    @Test
+    public void nullDestMatcherMatch() {
+        message = MessageBuilder
+                .withPayload("Hi")
+                .setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.CONNECT)
+                .build();
+
+         messages
+             .nullDestMatcher().denyAll()
+             .anyMessage().permitAll();
+
+        assertThat(getAttribute()).isEqualTo("denyAll");
+    }
+
+    @Test
+    public void simpTypeMatchersMatch() {
+         messages
+             .simpTypeMatchers(SimpMessageType.MESSAGE).denyAll()
+             .anyMessage().permitAll();
+
+        assertThat(getAttribute()).isEqualTo("denyAll");
+    }
+
+    @Test
+    public void simpTypeMatchersMatchMulti() {
+         messages
+             .simpTypeMatchers(SimpMessageType.CONNECT, SimpMessageType.MESSAGE).denyAll()
+             .anyMessage().permitAll();
+
+        assertThat(getAttribute()).isEqualTo("denyAll");
+    }
+
+    @Test
+    public void simpTypeMatchersNotMatch() {
+         messages
+             .simpTypeMatchers(SimpMessageType.CONNECT).denyAll()
+             .anyMessage().permitAll();
+
+        assertThat(getAttribute()).isEqualTo("permitAll");
+    }
+
+    @Test
+    public void simpTypeMatchersNotMatchMulti() {
+         messages
+             .simpTypeMatchers(SimpMessageType.CONNECT, SimpMessageType.DISCONNECT).denyAll()
+             .anyMessage().permitAll();
+
+        assertThat(getAttribute()).isEqualTo("permitAll");
+    }
+
     private String getAttribute() {
         MessageSecurityMetadataSource source = messages.createMetadataSource();
         Collection<ConfigAttribute> attrs = source.getAttributes(message);

+ 47 - 9
messaging/src/main/java/org/springframework/security/messaging/util/matcher/SimpDestinationMessageMatcher.java

@@ -33,7 +33,15 @@ import org.springframework.util.PathMatcher;
  * @author Rob Winch
  */
 public final class SimpDestinationMessageMatcher implements MessageMatcher<Object> {
+    public static final MessageMatcher<Object> NULL_DESTINATION_MATCHER = new MessageMatcher<Object>() {
+        public boolean matches(Message<? extends Object> message) {
+            String destination = SimpMessageHeaderAccessor.getDestination(message.getHeaders());
+            return destination == null;
+        }
+    };
+
     private final PathMatcher matcher;
+
     /**
      * The {@link MessageMatcher} that determines if the type matches. If the
      * type was null, this matcher will match every Message.
@@ -76,19 +84,16 @@ public final class SimpDestinationMessageMatcher implements MessageMatcher<Objec
         this(pattern, null);
     }
 
-
     /**
      * <p>
-     * Creates a new instance with the specified pattern and a {@link AntPathMatcher} created from the default
-     * constructor.
+     * Creates a new instance with the specified pattern and {@link PathMatcher}.
      * </p>
      *
      * @param pattern the pattern to use
-     * @param type the {@link SimpMessageType} to match on or null if any {@link SimpMessageType} should be matched.
      * @param pathMatcher the {@link PathMatcher} to use.
      */
-    public SimpDestinationMessageMatcher(String pattern, SimpMessageType type) {
-        this(pattern, type, new AntPathMatcher());
+    public SimpDestinationMessageMatcher(String pattern, PathMatcher pathMatcher) {
+        this(pattern, null, pathMatcher);
     }
 
     /**
@@ -100,9 +105,13 @@ public final class SimpDestinationMessageMatcher implements MessageMatcher<Objec
      * @param type the {@link SimpMessageType} to match on or null if any {@link SimpMessageType} should be matched.
      * @param pathMatcher the {@link PathMatcher} to use.
      */
-    public SimpDestinationMessageMatcher(String pattern, SimpMessageType type, PathMatcher pathMatcher) {
+    private SimpDestinationMessageMatcher(String pattern, SimpMessageType type, PathMatcher pathMatcher) {
         Assert.notNull(pattern, "pattern cannot be null");
         Assert.notNull(pathMatcher, "pathMatcher cannot be null");
+        if(!isTypeWithDestination(type)) {
+            throw new IllegalArgumentException("SimpMessageType " + type + " does not contain a destination and so cannot be matched on.");
+        }
+
         this.matcher = pathMatcher;
         this.messageTypeMatcher = type == null ? ANY_MESSAGE : new SimpMessageTypeMatcher(type);
         this.pattern = pattern;
@@ -117,16 +126,45 @@ public final class SimpDestinationMessageMatcher implements MessageMatcher<Objec
         return destination != null && matcher.match(pattern, destination);
     }
 
-
     public MessageMatcher<Object> getMessageTypeMatcher() {
         return messageTypeMatcher;
     }
 
-
     @Override
     public String toString() {
         return "SimpDestinationMessageMatcher [matcher=" + matcher
                 + ", messageTypeMatcher=" + messageTypeMatcher + ", pattern="
                 + pattern + "]";
     }
+
+    private boolean isTypeWithDestination(SimpMessageType type) {
+        if(type == null) {
+            return true;
+        }
+        return SimpMessageType.MESSAGE.equals(type) || SimpMessageType.SUBSCRIBE.equals(type);
+    }
+
+    /**
+     * <p>
+     * Creates a new instance with the specified pattern, {@code SimpMessageType.SUBSCRIBE}, and {@link PathMatcher}.
+     * </p>
+     *
+     * @param pattern the pattern to use
+     * @param pathMatcher the {@link PathMatcher} to use.
+     */
+    public static SimpDestinationMessageMatcher createSubscribeMatcher(String pattern, PathMatcher matcher) {
+        return new SimpDestinationMessageMatcher(pattern, SimpMessageType.SUBSCRIBE, matcher);
+    }
+
+    /**
+     * <p>
+     * Creates a new instance with the specified pattern, {@code SimpMessageType.MESSAGE}, and {@link PathMatcher}.
+     * </p>
+     *
+     * @param pattern the pattern to use
+     * @param pathMatcher the {@link PathMatcher} to use.
+     */
+    public static SimpDestinationMessageMatcher createMessageMatcher(String pattern, PathMatcher matcher) {
+        return new SimpDestinationMessageMatcher(pattern, SimpMessageType.MESSAGE, matcher);
+    }
 }

+ 20 - 5
messaging/src/test/java/org/springframework/security/messaging/util/matcher/SimpDestinationMessageMatcherTests.java

@@ -22,6 +22,8 @@ import org.junit.Test;
 import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
 import org.springframework.messaging.simp.SimpMessageType;
 import org.springframework.messaging.support.MessageBuilder;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PathMatcher;
 
 
 public class SimpDestinationMessageMatcherTests {
@@ -29,10 +31,13 @@ public class SimpDestinationMessageMatcherTests {
 
     SimpDestinationMessageMatcher matcher;
 
+    PathMatcher pathMatcher;
+
     @Before
     public void setup() {
         messageBuilder = MessageBuilder.withPayload("M");
         matcher = new SimpDestinationMessageMatcher("/**");
+        pathMatcher = new AntPathMatcher();
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -72,7 +77,7 @@ public class SimpDestinationMessageMatcherTests {
 
     @Test
     public void matchesFalseMessageTypeNotDisconnectType() throws Exception {
-        matcher = new SimpDestinationMessageMatcher("/match", SimpMessageType.MESSAGE);
+        matcher = SimpDestinationMessageMatcher.createMessageMatcher("/match", pathMatcher);
 
         messageBuilder.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.DISCONNECT);
 
@@ -81,7 +86,7 @@ public class SimpDestinationMessageMatcherTests {
 
     @Test
     public void matchesTrueMessageType() throws Exception {
-        matcher = new SimpDestinationMessageMatcher("/match", SimpMessageType.MESSAGE);
+        matcher = SimpDestinationMessageMatcher.createMessageMatcher("/match", pathMatcher);
 
         messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER,"/match");
         messageBuilder.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.MESSAGE);
@@ -89,9 +94,19 @@ public class SimpDestinationMessageMatcherTests {
         assertThat(matcher.matches(messageBuilder.build())).isTrue();
     }
 
+    @Test
+    public void matchesTrueSubscribeType() throws Exception {
+        matcher = SimpDestinationMessageMatcher.createSubscribeMatcher("/match", pathMatcher);
+
+        messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER,"/match");
+        messageBuilder.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.SUBSCRIBE);
+
+        assertThat(matcher.matches(messageBuilder.build())).isTrue();
+    }
+
     @Test
     public void matchesNullMessageType() throws Exception {
-        matcher = new SimpDestinationMessageMatcher("/match", null);
+        matcher = new SimpDestinationMessageMatcher("/match");
 
         messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER,"/match");
         messageBuilder.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.MESSAGE);
@@ -101,11 +116,11 @@ public class SimpDestinationMessageMatcherTests {
 
     @Test
     public void typeConstructorParameterIsTransmitted() throws Exception {
-        matcher = new SimpDestinationMessageMatcher("/match", SimpMessageType.MESSAGE);
+        matcher = SimpDestinationMessageMatcher.createMessageMatcher("/match", pathMatcher);
 
         MessageMatcher<Object> expectedTypeMatcher = new SimpMessageTypeMatcher(SimpMessageType.MESSAGE);
 
-	assertThat(matcher.getMessageTypeMatcher()).isEqualTo(expectedTypeMatcher);
+        assertThat(matcher.getMessageTypeMatcher()).isEqualTo(expectedTypeMatcher);
 
     }