Skip to content

Commit df19926

Browse files
author
Rob Winch
committed
SEC-2864: Default Spring Security WebSocket PathMatcher (fails Circular Bean)
1 parent 4c0ad5f commit df19926

File tree

2 files changed

+84
-9
lines changed

2 files changed

+84
-9
lines changed

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

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,19 @@
1515
*/
1616
package org.springframework.security.config.annotation.web.socket;
1717

18+
import java.util.ArrayList;
19+
import java.util.List;
20+
import java.util.Map;
21+
22+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
1823
import org.springframework.beans.factory.SmartInitializingSingleton;
1924
import org.springframework.beans.factory.annotation.Autowired;
2025
import org.springframework.context.ApplicationContext;
2126
import org.springframework.context.annotation.Bean;
2227
import org.springframework.core.Ordered;
2328
import org.springframework.core.annotation.Order;
2429
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
30+
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
2531
import org.springframework.messaging.simp.config.ChannelRegistration;
2632
import org.springframework.security.access.AccessDecisionVoter;
2733
import org.springframework.security.access.vote.AffirmativeBased;
@@ -33,6 +39,7 @@
3339
import org.springframework.security.messaging.context.SecurityContextChannelInterceptor;
3440
import org.springframework.security.messaging.web.csrf.CsrfChannelInterceptor;
3541
import org.springframework.security.messaging.web.socket.server.CsrfTokenHandshakeInterceptor;
42+
import org.springframework.util.PathMatcher;
3643
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
3744
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
3845
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@@ -42,10 +49,6 @@
4249
import org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler;
4350
import org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService;
4451

45-
import java.util.ArrayList;
46-
import java.util.List;
47-
import java.util.Map;
48-
4952
/**
5053
* Allows configuring WebSocket Authorization.
5154
*
@@ -57,7 +60,7 @@
5760
* @Configuration
5861
* public class WebSocketSecurityConfig extends
5962
* AbstractSecurityWebSocketMessageBrokerConfigurer {
60-
*
63+
*
6164
* @Override
6265
* protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
6366
* messages.simpDestMatchers("/user/queue/errors").permitAll()
@@ -94,11 +97,23 @@ public final void configureClientInboundChannel(ChannelRegistration registration
9497
registration.setInterceptors(csrfChannelInterceptor());
9598
}
9699
if (inboundRegistry.containsMapping()) {
100+
PathMatcher pathMatcher = getDefaultPathMatcher();
101+
if(pathMatcher != null) {
102+
inboundRegistry.simpDestPathMatcher(pathMatcher);
103+
}
97104
registration.setInterceptors(inboundChannelSecurity);
98105
}
99106
customizeClientInboundChannel(registration);
100107
}
101108

109+
private PathMatcher getDefaultPathMatcher() {
110+
try {
111+
return context.getBean(SimpAnnotationMethodMessageHandler.class).getPathMatcher();
112+
} catch(NoSuchBeanDefinitionException e) {
113+
return null;
114+
}
115+
}
116+
102117
/**
103118
* <p>
104119
* Determines if a CSRF token is required for connecting. This protects against remote

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

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
32
*
43
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
54
* use this file except in compliance with the License. You may obtain a copy of
@@ -17,7 +16,6 @@
1716

1817
import org.junit.After;
1918
import org.junit.Before;
20-
2119
import org.junit.Test;
2220
import org.springframework.context.annotation.Bean;
2321
import org.springframework.context.annotation.Configuration;
@@ -46,6 +44,7 @@
4644
import org.springframework.security.web.csrf.MissingCsrfTokenException;
4745
import org.springframework.stereotype.Controller;
4846
import org.springframework.test.util.ReflectionTestUtils;
47+
import org.springframework.util.AntPathMatcher;
4948
import org.springframework.web.HttpRequestHandler;
5049
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
5150
import org.springframework.web.servlet.HandlerMapping;
@@ -59,6 +58,7 @@
5958
import org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession;
6059

6160
import javax.servlet.http.HttpServletRequest;
61+
6262
import java.util.HashMap;
6363
import java.util.Map;
6464

@@ -232,6 +232,58 @@ public void messagesConnectWebSocketUseCsrfTokenHandshakeInterceptor()
232232
assertHandshake(request);
233233
}
234234

235+
@Test
236+
public void msmsRegistryCustomPatternMatcher()
237+
throws Exception {
238+
loadConfig(MsmsRegistryCustomPatternMatcherConfig.class);
239+
240+
clientInboundChannel().send(message("/app/a.b"));
241+
242+
try {
243+
clientInboundChannel().send(message("/app/a.b.c"));
244+
fail("Expected Exception");
245+
}
246+
catch (MessageDeliveryException expected) {
247+
assertThat(expected.getCause()).isInstanceOf(AccessDeniedException.class);
248+
}
249+
}
250+
251+
@Configuration
252+
@EnableWebSocketMessageBroker
253+
@Import(SyncExecutorConfig.class)
254+
static class MsmsRegistryCustomPatternMatcherConfig extends
255+
AbstractSecurityWebSocketMessageBrokerConfigurer {
256+
257+
public void registerStompEndpoints(StompEndpointRegistry registry) {
258+
registry.addEndpoint("/other").setHandshakeHandler(testHandshakeHandler())
259+
.withSockJS().setInterceptors(new HttpSessionHandshakeInterceptor());
260+
261+
registry.addEndpoint("/chat").setHandshakeHandler(testHandshakeHandler())
262+
.withSockJS().setInterceptors(new HttpSessionHandshakeInterceptor());
263+
}
264+
265+
// @formatter:off
266+
@Override
267+
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
268+
messages
269+
.simpDestMatchers("/app/a.*").permitAll()
270+
.anyMessage().denyAll();
271+
}
272+
// @formatter:on
273+
274+
@Override
275+
public void configureMessageBroker(MessageBrokerRegistry registry) {
276+
registry.setPathMatcher(new AntPathMatcher("."));
277+
registry.enableSimpleBroker("/queue/", "/topic/");
278+
registry.setApplicationDestinationPrefixes("/app");
279+
}
280+
281+
@Bean
282+
public TestHandshakeHandler testHandshakeHandler() {
283+
return new TestHandshakeHandler();
284+
}
285+
}
286+
235287
private void assertHandshake(HttpServletRequest request) {
236288
TestHandshakeHandler handshakeHandler = context
237289
.getBean(TestHandshakeHandler.class);
@@ -358,10 +410,14 @@ public void registerStompEndpoints(StompEndpointRegistry registry) {
358410
.withSockJS().setInterceptors(new HttpSessionHandshakeInterceptor());
359411
}
360412

413+
// @formatter:off
361414
@Override
362415
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
363-
messages.simpDestMatchers("/permitAll/**").permitAll().anyMessage().denyAll();
416+
messages
417+
.simpDestMatchers("/permitAll/**").permitAll()
418+
.anyMessage().denyAll();
364419
}
420+
// @formatter:on
365421

366422
@Override
367423
public void configureMessageBroker(MessageBrokerRegistry registry) {
@@ -431,10 +487,14 @@ public void registerStompEndpoints(StompEndpointRegistry registry) {
431487
.addInterceptors(new HttpSessionHandshakeInterceptor());
432488
}
433489

490+
// @formatter:off
434491
@Override
435492
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
436-
messages.simpDestMatchers("/permitAll/**").permitAll().anyMessage().denyAll();
493+
messages
494+
.simpDestMatchers("/permitAll/**").permitAll()
495+
.anyMessage().denyAll();
437496
}
497+
// @formatter:on
438498

439499
@Bean
440500
public TestHandshakeHandler testHandshakeHandler() {

0 commit comments

Comments
 (0)