Prechádzať zdrojové kódy

WebSocketMessageBrokerConfigTests groovy->java

Of note is that this commit unrolls three Spock @Unroll-parameterized
tests into a separate test for each parameter.

Issue: gh-4939
Josh Cummings 7 rokov pred
rodič
commit
ec46b7dbe1
25 zmenil súbory, kde vykonal 1315 pridanie a 560 odobranie
  1. 0 560
      config/src/test/groovy/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests.groovy
  2. 547 0
      config/src/test/java/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests.java
  3. 31 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-ConnectAckInterceptTypeConfig.xml
  4. 31 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-ConnectInterceptTypeConfig.xml
  5. 33 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomExpressionHandlerConfig.xml
  6. 45 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomInterceptorConfig.xml
  7. 46 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomPathMatcherConfig.xml
  8. 31 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-DisconnectAckInterceptTypeConfig.xml
  9. 31 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-DisconnectInterceptTypeConfig.xml
  10. 31 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-HeartbeatInterceptTypeConfig.xml
  11. 30 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-IdConfig.xml
  12. 42 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-IdIntegratedConfig.xml
  13. 31 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-MessageInterceptTypeConfig.xml
  14. 31 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-NoIdConfig.xml
  15. 31 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-OtherInterceptTypeConfig.xml
  16. 31 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SubscribeInterceptTypeConfig.xml
  17. 31 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SyncConfig.xml
  18. 50 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SyncCustomArgumentResolverConfig.xml
  19. 31 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SyncSameOriginDisabledConfig.xml
  20. 31 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SyncSockJsConfig.xml
  21. 31 0
      config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-UnsubscribeInterceptTypeConfig.xml
  22. 24 0
      config/src/test/resources/org/springframework/security/config/websocket/controllers.xml
  23. 22 0
      config/src/test/resources/org/springframework/security/config/websocket/sync.xml
  24. 37 0
      config/src/test/resources/org/springframework/security/config/websocket/websocket-sockjs.xml
  25. 36 0
      config/src/test/resources/org/springframework/security/config/websocket/websocket.xml

+ 0 - 560
config/src/test/groovy/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests.groovy

@@ -1,560 +0,0 @@
-package org.springframework.security.config.websocket
-
-import static org.mockito.Mockito.*
-
-import org.springframework.beans.BeansException
-import org.springframework.beans.factory.config.BeanDefinition
-import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
-import org.springframework.beans.factory.parsing.BeanDefinitionParsingException
-import org.springframework.beans.factory.support.BeanDefinitionRegistry
-import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
-import org.springframework.beans.factory.support.RootBeanDefinition
-import org.springframework.core.MethodParameter
-import org.springframework.core.task.SyncTaskExecutor
-import org.springframework.http.server.ServerHttpRequest
-import org.springframework.http.server.ServerHttpResponse
-import org.springframework.messaging.Message
-import org.springframework.messaging.MessageDeliveryException
-import org.springframework.messaging.handler.annotation.MessageMapping
-import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver
-import org.springframework.messaging.simp.SimpMessageHeaderAccessor
-import org.springframework.messaging.simp.SimpMessageType
-import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler
-import org.springframework.messaging.support.ChannelInterceptor
-import org.springframework.messaging.support.GenericMessage
-import org.springframework.mock.web.MockHttpServletRequest
-import org.springframework.mock.web.MockHttpServletResponse
-import org.springframework.security.access.AccessDeniedException
-import org.springframework.security.access.expression.SecurityExpressionOperations;
-import org.springframework.security.authentication.TestingAuthenticationToken
-import org.springframework.security.config.AbstractXmlConfigTests
-import org.springframework.security.core.Authentication
-import org.springframework.security.core.annotation.AuthenticationPrincipal
-import org.springframework.security.core.context.SecurityContextHolder
-import org.springframework.security.messaging.access.expression.DefaultMessageSecurityExpressionHandler;
-import org.springframework.security.messaging.access.expression.MessageSecurityExpressionRoot;
-import org.springframework.security.web.csrf.CsrfToken
-import org.springframework.security.web.csrf.DefaultCsrfToken
-import org.springframework.security.web.csrf.InvalidCsrfTokenException
-import org.springframework.stereotype.Controller
-import org.springframework.util.AntPathMatcher
-import org.springframework.web.servlet.HandlerMapping
-import org.springframework.web.socket.WebSocketHandler
-import org.springframework.web.socket.server.HandshakeFailureException
-import org.springframework.web.socket.server.HandshakeHandler
-import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor
-import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler
-import org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler
-import org.springframework.web.socket.sockjs.transport.handler.SockJsWebSocketHandler
-
-import spock.lang.Unroll
-
-/**
- *
- * @author Rob Winch
- */
-class WebSocketMessageBrokerConfigTests extends AbstractXmlConfigTests {
-	Authentication messageUser = new TestingAuthenticationToken('user','pass','ROLE_USER')
-	boolean useSockJS = false
-	CsrfToken csrfToken = new DefaultCsrfToken('headerName', 'paramName', 'token')
-
-	def cleanup() {
-		SecurityContextHolder.clearContext()
-	}
-
-	def 'websocket with no id automatically integrates with clientInboundChannel'() {
-		setup:
-		websocket {
-			'intercept-message'(pattern:'/permitAll',access:'permitAll')
-			'intercept-message'(pattern:'/denyAll',access:'denyAll')
-		}
-
-
-		when: 'message is sent to the denyAll endpoint'
-		clientInboundChannel.send(message('/denyAll'))
-
-		then: 'access is denied to the denyAll endpoint'
-		def e = thrown(MessageDeliveryException)
-		e.cause instanceof AccessDeniedException
-
-		and: 'access is granted to the permitAll endpoint'
-		clientInboundChannel.send(message('/permitAll'))
-	}
-
-	def 'anonymous authentication supported'() {
-		setup:
-		websocket {
-			'intercept-message'(pattern:'/permitAll',access:'permitAll')
-			'intercept-message'(pattern:'/denyAll',access:'denyAll')
-		}
-		messageUser = null
-
-		when: 'message is sent to the permitAll endpoint with no user'
-		clientInboundChannel.send(message('/permitAll'))
-
-		then: 'access is granted'
-		noExceptionThrown()
-	}
-
-	@Unroll
-	def "message type - #type"(SimpMessageType type) {
-		setup:
-		websocket {
-			'intercept-message'('type': type.toString(), access:'permitAll')
-			'intercept-message'(pattern:'/**', access:'denyAll')
-		}
-		messageUser = null
-		SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(type)
-		if(SimpMessageType.CONNECT == type) {
-			headers.setNativeHeader(csrfToken.headerName, csrfToken.token)
-		}
-		Message message = message(headers, '/permitAll')
-
-		when: 'message is sent to the permitAll endpoint with no user'
-		clientInboundChannel.send(message)
-
-		then: 'access is granted'
-		noExceptionThrown()
-
-		where:
-		type << SimpMessageType.values()
-	}
-
-	@Unroll
-	def "pattern and message type - #type"(SimpMessageType type) {
-		setup:
-		websocket {
-			'intercept-message'(pattern: '/permitAll', 'type': type.toString(), access:'permitAll')
-			'intercept-message'(pattern:'/**', access:'denyAll')
-		}
-
-		when: 'message is sent to the permitAll endpoint with no user'
-		clientInboundChannel.send(message('/permitAll', type))
-
-		then: 'access is granted'
-		noExceptionThrown()
-
-		when: 'message sent to other message type'
-		clientInboundChannel.send(message('/permitAll', SimpMessageType.UNSUBSCRIBE))
-
-		then: 'does not match'
-		MessageDeliveryException e = thrown()
-		e.cause instanceof AccessDeniedException
-
-		when: 'message is sent to other pattern'
-		clientInboundChannel.send(message('/other', type))
-
-		then: 'does not match'
-		MessageDeliveryException eOther = thrown()
-		eOther.cause instanceof AccessDeniedException
-
-		where:
-		type << [SimpMessageType.MESSAGE, SimpMessageType.SUBSCRIBE]
-	}
-
-	@Unroll
-	def "intercept-message with invalid type and pattern -  #type"(SimpMessageType type) {
-		when:
-		websocket {
-			'intercept-message'(pattern : '/**', 'type': type.toString(),  access:'permitAll')
-		}
-		then:
-		thrown(BeanDefinitionParsingException)
-
-		where:
-		type << [SimpMessageType.CONNECT, SimpMessageType.CONNECT_ACK, SimpMessageType.DISCONNECT, SimpMessageType.DISCONNECT_ACK, SimpMessageType.HEARTBEAT, SimpMessageType.OTHER, SimpMessageType.UNSUBSCRIBE ]
-	}
-
-	def 'messages with no id automatically adds Authentication argument resolver'() {
-		setup:
-		def id = 'authenticationController'
-		bean(id,MyController)
-		bean('inPostProcessor',InboundExecutorPostProcessor)
-		websocket {
-			'intercept-message'(pattern:'/**',access:'permitAll')
-		}
-
-		when: 'message is sent to the authentication endpoint'
-		clientInboundChannel.send(message('/authentication'))
-
-		then: 'the AuthenticationPrincipal is resolved'
-		def controller = appContext.getBean(id)
-		controller.authenticationPrincipal == messageUser.name
-	}
-
-	def 'messages of type CONNECT use CsrfTokenHandshakeInterceptor'() {
-		setup:
-		def id = 'authenticationController'
-		bean(id,MyController)
-		bean('inPostProcessor',InboundExecutorPostProcessor)
-		websocket {
-			'intercept-message'(pattern:'/**',access:'permitAll')
-		}
-
-		SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT)
-		Message<?> message = message(headers,'/authentication')
-		WebSocketHttpRequestHandler handler = appContext.getBean(WebSocketHttpRequestHandler)
-		MockHttpServletRequest request = new MockHttpServletRequest()
-		String sessionAttr = "sessionAttr"
-		request.getSession().setAttribute(sessionAttr,"sessionValue")
-
-		CsrfToken token = new DefaultCsrfToken("header", "param", "token")
-		request.setAttribute(CsrfToken.name, token)
-
-		when:
-		handler.handleRequest(request , new MockHttpServletResponse())
-		TestHandshakeHandler handshakeHandler = appContext.getBean(TestHandshakeHandler)
-
-		then: 'CsrfToken is populated'
-		handshakeHandler.attributes.get(CsrfToken.name) == token
-
-		and: 'Explicitly listed HandshakeInterceptor are not overridden'
-		handshakeHandler.attributes.get(sessionAttr) == request.getSession().getAttribute(sessionAttr)
-	}
-
-	def 'messages of type CONNECT use CsrfTokenHandshakeInterceptor with SockJS'() {
-		setup:
-		useSockJS = true
-		def id = 'authenticationController'
-		bean(id,MyController)
-		bean('inPostProcessor',InboundExecutorPostProcessor)
-		websocket {
-			'intercept-message'(pattern:'/**',access:'permitAll')
-		}
-
-		SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT)
-		Message<?> message = message(headers,'/authentication')
-		SockJsHttpRequestHandler handler = appContext.getBean(SockJsHttpRequestHandler)
-		MockHttpServletRequest request = new MockHttpServletRequest()
-		String sessionAttr = "sessionAttr"
-		request.getSession().setAttribute(sessionAttr,"sessionValue")
-
-		CsrfToken token = new DefaultCsrfToken("header", "param", "token")
-		request.setAttribute(CsrfToken.name, token)
-
-		request.setMethod("GET")
-		request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "/289/tpyx6mde/websocket")
-
-		when:
-		handler.handleRequest(request , new MockHttpServletResponse())
-		TestHandshakeHandler handshakeHandler = appContext.getBean(TestHandshakeHandler)
-
-		then: 'CsrfToken is populated'
-		handshakeHandler.attributes?.get(CsrfToken.name) == token
-
-		and: 'Explicitly listed HandshakeInterceptor are not overridden'
-		handshakeHandler.attributes?.get(sessionAttr) == request.getSession().getAttribute(sessionAttr)
-	}
-
-	def 'messages of type CONNECT require valid CsrfToken'() {
-		setup:
-		def id = 'authenticationController'
-		bean(id,MyController)
-		bean('inPostProcessor',InboundExecutorPostProcessor)
-		websocket {
-			'intercept-message'(pattern:'/**',access:'permitAll')
-		}
-
-		when: 'websocket of type CONNECTION is sent without CsrfTOken'
-		SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT)
-		Message<?> message = message(headers,'/authentication')
-		clientInboundChannel.send(message)
-
-		then: 'CSRF Protection blocks the Message'
-		MessageDeliveryException expected = thrown()
-		expected.cause instanceof InvalidCsrfTokenException
-	}
-
-	def 'messages of type CONNECT disabled valid CsrfToken'() {
-		setup:
-		def id = 'authenticationController'
-		bean(id,MyController)
-		bean('inPostProcessor',InboundExecutorPostProcessor)
-		websocket('same-origin-disabled':true) {
-			'intercept-message'(pattern:'/**',access:'permitAll')
-		}
-
-		when: 'websocket of type CONNECTION is sent without CsrfTOken'
-		SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT)
-		Message<?> message = message(headers,'/authentication')
-		clientInboundChannel.send(message)
-
-		then: 'CSRF Protection blocks the Message'
-		noExceptionThrown()
-	}
-
-	def 'websocket with no id does not override customArgumentResolvers'() {
-		setup:
-		def id = 'authenticationController'
-		bean(id,MyController)
-		bean('inPostProcessor',InboundExecutorPostProcessor)
-		bean('mcar', MyCustomArgumentResolver)
-		xml.'websocket:message-broker' {
-			'websocket:transport' {}
-			'websocket:stomp-endpoint'(path:'/app') {
-				'websocket:handshake-handler'(ref:'testHandler') {}
-			}
-			'websocket:simple-broker'(prefix:"/queue, /topic"){}
-			'websocket:argument-resolvers' {
-				'b:ref'(bean:'mcar')
-			}
-		}
-		websocket {
-			'intercept-message'(pattern:'/**',access:'permitAll')
-		}
-
-		when: 'websocket is sent to the myCustom endpoint'
-		clientInboundChannel.send(message('/myCustom'))
-
-		then: 'myCustomArgument is resolved'
-		def controller = appContext.getBean(id)
-		controller.myCustomArgument!= null
-	}
-
-	def 'websocket defaults pathMatcher'() {
-		setup:
-		bean('pathMatcher',AntPathMatcher.name,['.'])
-		bean('testHandler', TestHandshakeHandler)
-		xml.'websocket:message-broker'('path-matcher':'pathMatcher') {
-			'websocket:transport' {}
-			'websocket:stomp-endpoint'(path:'/app') {
-				'websocket:handshake-handler'(ref:'testHandler') {}
-			}
-			'websocket:simple-broker'(prefix:"/queue, /topic"){}
-		}
-		xml.'websocket-message-broker' {
-			'intercept-message'(pattern:'/denyAll.*',access:'denyAll')
-		}
-		createAppContext()
-
-		when: 'sent to denyAll.a'
-		appContext.getBean(SimpAnnotationMethodMessageHandler)
-		clientInboundChannel.send(message('/denyAll.a'))
-
-		then: 'access is denied'
-		MessageDeliveryException expected = thrown()
-		expected.cause instanceof AccessDeniedException
-
-		when: 'sent to denyAll.a.b'
-		clientInboundChannel.send(message('/denyAll.a.b'))
-
-		then: 'access is allowed'
-		noExceptionThrown()
-	}
-
-	def 'websocket with id does not integrate with clientInboundChannel'() {
-		setup:
-		websocket([id:'inCsi']) {
-			'intercept-message'(pattern:'/**',access:'denyAll')
-		}
-
-		when:
-		def success = clientInboundChannel.send(message('/denyAll'))
-
-		then:
-		success
-
-	}
-
-	def 'websocket with id can be explicitly integrated with clientInboundChannel'() {
-		setup: 'websocket security explicitly setup'
-		xml.'websocket:message-broker' {
-			'websocket:transport' {}
-			'websocket:stomp-endpoint'(path:'/app') {
-				'websocket:sockjs' {}
-			}
-			'websocket:simple-broker'(prefix:"/queue, /topic"){}
-			'websocket:client-inbound-channel' {
-				'websocket:interceptors' {
-					'b:bean'(class:'org.springframework.security.messaging.context.SecurityContextChannelInterceptor'){}
-					'b:ref'(bean:'inCsi'){}
-				}
-			}
-		}
-		xml.'websocket-message-broker'(id:'inCsi') {
-			'intercept-message'(pattern:'/**',access:'denyAll')
-		}
-		createAppContext()
-
-		when:
-		clientInboundChannel.send(message('/denyAll'))
-
-		then:
-		def e = thrown(MessageDeliveryException)
-		e.cause instanceof AccessDeniedException
-
-	}
-
-	def 'automatic integration with clientInboundChannel does not override exisiting websocket:interceptors'() {
-		setup:
-		mockBean(ChannelInterceptor,'mci')
-		xml.'websocket:message-broker'('application-destination-prefix':'/app',
-										'user-destination-prefix':'/user') {
-			'websocket:transport' {}
-			'websocket:stomp-endpoint'(path:'/foo') {
-				'websocket:sockjs' {}
-			}
-			'websocket:simple-broker'(prefix:"/queue, /topic"){}
-			'websocket:client-inbound-channel' {
-				'websocket:interceptors' {
-					'b:ref'(bean:'mci'){}
-				}
-			}
-		}
-		xml.'websocket-message-broker' {
-			'intercept-message'(pattern:'/denyAll',access:'denyAll')
-			'intercept-message'(pattern:'/permitAll',access:'permitAll')
-		}
-		createAppContext()
-		ChannelInterceptor mci = appContext.getBean('mci')
-		when:
-		Message<?> message = message('/permitAll')
-		clientInboundChannel.send(message)
-
-		then:
-		verify(mci).preSend(message, clientInboundChannel) || true
-
-	}
-
-	def websocket(Map<String,Object> attrs=[:], Closure c) {
-		bean('testHandler', TestHandshakeHandler)
-		xml.'websocket:message-broker' {
-			'websocket:transport' {}
-			'websocket:stomp-endpoint'(path:'/app') {
-				'websocket:handshake-handler'(ref:'testHandler') {}
-				'websocket:handshake-interceptors' {
-					'b:bean'('class':HttpSessionHandshakeInterceptor.name) {}
-				}
-				if(useSockJS) {
-					'websocket:sockjs' {}
-				}
-			}
-			'websocket:simple-broker'(prefix:"/queue, /topic"){}
-		}
-		xml.'websocket-message-broker'(attrs, c)
-		createAppContext()
-	}
-
-	def 'custom expressions'() {
-		setup:
-		bean('expressionHandler', DenyRobMessageSecurityExpressionHandler)
-		websocket {
-			'expression-handler' (ref: 'expressionHandler') {}
-			'intercept-message'(pattern:'/**',access:'denyRob()')
-		}
-
-		when: 'message is sent with user'
-		clientInboundChannel.send(message('/message'))
-
-		then: 'access is allowed to custom expression'
-		noExceptionThrown()
-
-		when:
-		messageUser = new TestingAuthenticationToken('rob', 'pass', 'ROLE_USER')
-		clientInboundChannel.send(message('/message'))
-
-		then:
-		def e = thrown(MessageDeliveryException)
-		e.cause instanceof AccessDeniedException
-	}
-
-	def getClientInboundChannel() {
-		appContext.getBean("clientInboundChannel")
-	}
-
-	def message(String destination, SimpMessageType type=SimpMessageType.MESSAGE) {
-		SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(type)
-		message(headers, destination)
-	}
-
-	def message(SimpMessageHeaderAccessor headers, String destination) {
-		headers.sessionId = '123'
-		headers.sessionAttributes = [:]
-		headers.destination = destination
-		if(messageUser != null) {
-			headers.user = messageUser
-		}
-		if(csrfToken != null) {
-			headers.sessionAttributes[CsrfToken.name] = csrfToken
-		}
-		new GenericMessage<String>("hi",headers.messageHeaders)
-	}
-
-	@Controller
-	static class MyController {
-		String authenticationPrincipal
-		MyCustomArgument myCustomArgument
-
-		@MessageMapping('/authentication')
-		public void authentication(@AuthenticationPrincipal String un) {
-			this.authenticationPrincipal = un
-		}
-
-		@MessageMapping('/myCustom')
-		public void myCustom(MyCustomArgument myCustomArgument) {
-			this.myCustomArgument = myCustomArgument
-		}
-	}
-
-	static class MyCustomArgument {
-		MyCustomArgument(String notDefaultConstr) {}
-	}
-
-	static class MyCustomArgumentResolver implements HandlerMethodArgumentResolver {
-
-		@Override
-		boolean supportsParameter(MethodParameter parameter) {
-			parameter.parameterType.isAssignableFrom(MyCustomArgument)
-		}
-
-		@Override
-		Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
-			new MyCustomArgument("")
-		}
-	}
-
-	static class TestHandshakeHandler implements HandshakeHandler {
-		Map<String, Object> attributes;
-
-		boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws HandshakeFailureException {
-			this.attributes = attributes
-			if(wsHandler instanceof SockJsWebSocketHandler) {
-				// work around SPR-12716
-				SockJsWebSocketHandler sockJs = (SockJsWebSocketHandler) wsHandler;
-				this.attributes = sockJs.sockJsSession.attributes
-			}
-			true
-		}
-	}
-
-	/**
-	 * Changes the clientInboundChannel Executor to be synchronous
-	 */
-	static class InboundExecutorPostProcessor implements BeanDefinitionRegistryPostProcessor {
-
-		@Override
-		void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
-			BeanDefinition inbound = registry.getBeanDefinition("clientInboundChannel")
-			inbound.getConstructorArgumentValues().addIndexedArgumentValue(0, new RootBeanDefinition(SyncTaskExecutor));
-		}
-
-		@Override
-		void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
-
-		}
-	}
-
-	static class DenyRobMessageSecurityExpressionHandler extends DefaultMessageSecurityExpressionHandler<Object> {
-		@Override
-		protected SecurityExpressionOperations createSecurityExpressionRoot(
-				Authentication authentication,
-				Message<Object> invocation) {
-			return new MessageSecurityExpressionRoot(authentication, invocation) {
-				public boolean denyRob() {
-					Authentication auth = getAuthentication();
-					return auth != null && !"rob".equals(auth.getName());
-				}
-			};
-		}
-	}
-}

+ 547 - 0
config/src/test/java/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests.java

@@ -0,0 +1,547 @@
+/*
+ * Copyright 2002-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.config.websocket;
+
+import org.assertj.core.api.ThrowableAssert;
+import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.core.MethodParameter;
+import org.springframework.core.task.SyncTaskExecutor;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.handler.annotation.MessageMapping;
+import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
+import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
+import org.springframework.messaging.simp.SimpMessageType;
+import org.springframework.messaging.support.ChannelInterceptorAdapter;
+import org.springframework.messaging.support.GenericMessage;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.access.expression.SecurityExpressionOperations;
+import org.springframework.security.config.test.SpringTestRule;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.messaging.access.expression.DefaultMessageSecurityExpressionHandler;
+import org.springframework.security.messaging.access.expression.MessageSecurityExpressionRoot;
+import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.security.web.csrf.CsrfToken;
+import org.springframework.security.web.csrf.DefaultCsrfToken;
+import org.springframework.security.web.csrf.InvalidCsrfTokenException;
+import org.springframework.stereotype.Controller;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.server.HandshakeFailureException;
+import org.springframework.web.socket.server.HandshakeHandler;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+
+/**
+ * @author Rob Winch
+ * @author Josh Cummings
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SecurityTestExecutionListeners
+public class WebSocketMessageBrokerConfigTests {
+	private static final String CONFIG_LOCATION_PREFIX =
+			"classpath:org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests";
+
+	@Rule
+	public final SpringTestRule spring = new SpringTestRule();
+
+	@Autowired(required = false)
+	private MessageChannel clientInboundChannel;
+
+	@Autowired(required = false)
+	private MessageController messageController;
+
+	@Autowired(required = false)
+	private MessageWithArgumentController messageWithArgumentController;
+
+	@Autowired(required = false)
+	private TestHandshakeHandler testHandshakeHandler;
+
+	private CsrfToken token = new DefaultCsrfToken("header", "param", "token");
+
+	@Test
+	public void sendWhenNoIdSpecifiedThenIntegratesWithClientInboundChannel() {
+		this.spring.configLocations(xml("NoIdConfig")).autowire();
+
+		this.clientInboundChannel.send(message("/permitAll"));
+
+		assertThatThrownBy(() -> this.clientInboundChannel.send(message("/denyAll")))
+				.hasCauseInstanceOf(AccessDeniedException.class);
+	}
+
+	@Test
+	public void sendWhenAnonymousMessageWithConnectMessageTypeThenPermitted() {
+		this.spring.configLocations(xml("NoIdConfig")).autowire();
+
+		SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);
+		headers.setNativeHeader(this.token.getHeaderName(), this.token.getToken());
+
+		assertThatCode(() -> this.clientInboundChannel.send(message("/permitAll", headers)))
+				.doesNotThrowAnyException();
+	}
+
+	@Test
+	public void sendWhenAnonymousMessageWithConnectAckMessageTypeThenPermitted() {
+		this.spring.configLocations(xml("NoIdConfig")).autowire();
+
+		Message<?> message = message("/permitAll", SimpMessageType.CONNECT_ACK);
+
+		assertThatCode(send(message)).doesNotThrowAnyException();
+	}
+
+	@Test
+	public void sendWhenAnonymousMessageWithDisconnectMessageTypeThenPermitted() {
+		this.spring.configLocations(xml("NoIdConfig")).autowire();
+
+		Message<?> message = message("/permitAll", SimpMessageType.DISCONNECT);
+
+		assertThatCode(send(message)).doesNotThrowAnyException();
+	}
+
+	@Test
+	public void sendWhenAnonymousMessageWithDisconnectAckMessageTypeThenPermitted() {
+		this.spring.configLocations(xml("NoIdConfig")).autowire();
+
+		Message<?> message = message("/permitAll", SimpMessageType.DISCONNECT_ACK);
+
+		assertThatCode(send(message)).doesNotThrowAnyException();
+	}
+
+	@Test
+	public void sendWhenAnonymousMessageWithHeartbeatMessageTypeThenPermitted() {
+		this.spring.configLocations(xml("NoIdConfig")).autowire();
+
+		Message<?> message = message("/permitAll", SimpMessageType.HEARTBEAT);
+
+		assertThatCode(send(message)).doesNotThrowAnyException();
+	}
+
+	@Test
+	public void sendWhenAnonymousMessageWithMessageMessageTypeThenPermitted() {
+		this.spring.configLocations(xml("NoIdConfig")).autowire();
+
+		Message<?> message = message("/permitAll", SimpMessageType.MESSAGE);
+
+		assertThatCode(send(message)).doesNotThrowAnyException();
+	}
+
+	@Test
+	public void sendWhenAnonymousMessageWithOtherMessageTypeThenPermitted() {
+		this.spring.configLocations(xml("NoIdConfig")).autowire();
+
+		Message<?> message = message("/permitAll", SimpMessageType.OTHER);
+
+		assertThatCode(send(message)).doesNotThrowAnyException();
+	}
+
+	@Test
+	public void sendWhenAnonymousMessageWithSubscribeMessageTypeThenPermitted() {
+		this.spring.configLocations(xml("NoIdConfig")).autowire();
+
+		Message<?> message = message("/permitAll", SimpMessageType.SUBSCRIBE);
+
+		assertThatCode(send(message)).doesNotThrowAnyException();
+	}
+
+	@Test
+	public void sendWhenAnonymousMessageWithUnsubscribeMessageTypeThenPermitted() {
+		this.spring.configLocations(xml("NoIdConfig")).autowire();
+
+		Message<?> message = message("/permitAll", SimpMessageType.UNSUBSCRIBE);
+
+		assertThatCode(send(message)).doesNotThrowAnyException();
+	}
+
+	@Test
+	public void sendWhenConnectWithoutCsrfTokenThenDenied() {
+		this.spring.configLocations(xml("SyncConfig")).autowire();
+
+		Message<?> message = message("/message", SimpMessageType.CONNECT);
+
+		assertThatThrownBy(send(message)).hasCauseInstanceOf(InvalidCsrfTokenException.class);
+	}
+
+	@Test
+	public void sendWhenConnectWithSameOriginDisabledThenCsrfTokenNotRequired() {
+		this.spring.configLocations(xml("SyncSameOriginDisabledConfig")).autowire();
+
+		Message<?> message = message("/message", SimpMessageType.CONNECT);
+
+		assertThatCode(send(message)).doesNotThrowAnyException();
+	}
+
+	@Test
+	public void sendWhenInterceptWiredForMessageTypeThenDeniesOnTypeMismatch() {
+		this.spring.configLocations(xml("MessageInterceptTypeConfig")).autowire();
+
+		Message<?> message = message("/permitAll", SimpMessageType.MESSAGE);
+
+		assertThatCode(send(message)).doesNotThrowAnyException();
+
+		message = message("/permitAll", SimpMessageType.UNSUBSCRIBE);
+
+		assertThatThrownBy(send(message)).hasCauseInstanceOf(AccessDeniedException.class);
+
+		message = message("/anyOther", SimpMessageType.MESSAGE);
+
+		assertThatThrownBy(send(message)).hasCauseInstanceOf(AccessDeniedException.class);
+	}
+
+	@Test
+	public void sendWhenInterceptWiredForSubscribeTypeThenDeniesOnTypeMismatch() {
+		this.spring.configLocations(xml("SubscribeInterceptTypeConfig")).autowire();
+
+		Message<?> message = message("/permitAll", SimpMessageType.SUBSCRIBE);
+
+		assertThatCode(send(message)).doesNotThrowAnyException();
+
+		message = message("/permitAll", SimpMessageType.UNSUBSCRIBE);
+
+		assertThatThrownBy(send(message)).hasCauseInstanceOf(AccessDeniedException.class);
+
+		message = message("/anyOther", SimpMessageType.SUBSCRIBE);
+
+		assertThatThrownBy(send(message)).hasCauseInstanceOf(AccessDeniedException.class);
+	}
+
+	// -- invalid intercept types -- //
+
+	@Test
+	public void configureWhenUsingConnectMessageTypeThenAutowireFails() {
+		ThrowingCallable bad = () ->
+				this.spring.configLocations(xml("ConnectInterceptTypeConfig")).autowire();
+
+		assertThatThrownBy(bad).isInstanceOf(BeanDefinitionParsingException.class);
+	}
+
+	@Test
+	public void configureWhenUsingConnectAckMessageTypeThenAutowireFails() {
+		ThrowingCallable bad = () ->
+				this.spring.configLocations(xml("ConnectAckInterceptTypeConfig")).autowire();
+
+		assertThatThrownBy(bad).isInstanceOf(BeanDefinitionParsingException.class);
+	}
+
+	@Test
+	public void configureWhenUsingDisconnectMessageTypeThenAutowireFails() {
+		ThrowingCallable bad = () ->
+				this.spring.configLocations(xml("DisconnectInterceptTypeConfig")).autowire();
+
+		assertThatThrownBy(bad).isInstanceOf(BeanDefinitionParsingException.class);
+	}
+
+	@Test
+	public void configureWhenUsingDisconnectAckMessageTypeThenAutowireFails() {
+		ThrowingCallable bad = () ->
+				this.spring.configLocations(xml("DisconnectAckInterceptTypeConfig")).autowire();
+
+		assertThatThrownBy(bad).isInstanceOf(BeanDefinitionParsingException.class);
+	}
+
+	@Test
+	public void configureWhenUsingHeartbeatMessageTypeThenAutowireFails() {
+		ThrowingCallable bad = () ->
+				this.spring.configLocations(xml("HeartbeatInterceptTypeConfig")).autowire();
+
+		assertThatThrownBy(bad).isInstanceOf(BeanDefinitionParsingException.class);
+	}
+
+	@Test
+	public void configureWhenUsingOtherMessageTypeThenAutowireFails() {
+		ThrowingCallable bad = () ->
+				this.spring.configLocations(xml("OtherInterceptTypeConfig")).autowire();
+
+		assertThatThrownBy(bad).isInstanceOf(BeanDefinitionParsingException.class);
+	}
+
+	@Test
+	public void configureWhenUsingUnsubscribeMessageTypeThenAutowireFails() {
+		ThrowingCallable bad = () ->
+				this.spring.configLocations(xml("UnsubscribeInterceptTypeConfig")).autowire();
+
+		assertThatThrownBy(bad).isInstanceOf(BeanDefinitionParsingException.class);
+	}
+
+	@Test
+	public void sendWhenNoIdMessageThenAuthenticationPrincipalResolved() throws Exception {
+		this.spring.configLocations(xml("SyncConfig")).autowire();
+
+		this.clientInboundChannel.send(message("/message"));
+
+		assertThat(this.messageController.username).isEqualTo("anonymous");
+	}
+
+	@Test
+	public void requestWhenConnectMessageThenUsesCsrfTokenHandshakeInterceptor() throws Exception {
+		this.spring.configLocations(xml("SyncConfig")).autowire();
+
+		WebApplicationContext context = (WebApplicationContext) this.spring.getContext();
+		MockMvc mvc = MockMvcBuilders.webAppContextSetup(context).build();
+
+		String csrfAttributeName = CsrfToken.class.getName();
+		String customAttributeName = this.getClass().getName();
+
+		MvcResult result = mvc.perform(get("/app")
+								.requestAttr(csrfAttributeName, this.token)
+								.sessionAttr(customAttributeName, "attributeValue"))
+							.andReturn();
+
+		CsrfToken handshakeToken = (CsrfToken) this.testHandshakeHandler.attributes.get(csrfAttributeName);
+		String handshakeValue = (String) this.testHandshakeHandler.attributes.get(customAttributeName);
+		String sessionValue = (String) result.getRequest().getSession().getAttribute(customAttributeName);
+
+		assertThat(handshakeToken).isEqualTo(this.token)
+				.withFailMessage("CsrfToken is populated");
+
+		assertThat(handshakeValue).isEqualTo(sessionValue)
+				.withFailMessage("Explicitly listed session variables are not overridden");
+	}
+
+	@Test
+	public void requestWhenConnectMessageAndUsingSockJsThenUsesCsrfTokenHandshakeInterceptor() throws Exception {
+		this.spring.configLocations(xml("SyncSockJsConfig")).autowire();
+
+		WebApplicationContext context = (WebApplicationContext) this.spring.getContext();
+		MockMvc mvc = MockMvcBuilders.webAppContextSetup(context).build();
+
+		String csrfAttributeName = CsrfToken.class.getName();
+		String customAttributeName = this.getClass().getName();
+
+		MvcResult result = mvc.perform(get("/app/289/tpyx6mde/websocket")
+								.requestAttr(csrfAttributeName, this.token)
+								.sessionAttr(customAttributeName, "attributeValue"))
+							.andReturn();
+
+		CsrfToken handshakeToken = (CsrfToken) this.testHandshakeHandler.attributes.get(csrfAttributeName);
+		String handshakeValue = (String) this.testHandshakeHandler.attributes.get(customAttributeName);
+		String sessionValue = (String) result.getRequest().getSession().getAttribute(customAttributeName);
+
+		assertThat(handshakeToken).isEqualTo(this.token)
+				.withFailMessage("CsrfToken is populated");
+
+		assertThat(handshakeValue).isEqualTo(sessionValue)
+				.withFailMessage("Explicitly listed session variables are not overridden");
+	}
+
+	@Test
+	public void sendWhenNoIdSpecifiedThenCustomArgumentResolversAreNotOverridden() {
+		this.spring.configLocations(xml("SyncCustomArgumentResolverConfig")).autowire();
+
+		this.clientInboundChannel.send(message("/message-with-argument"));
+
+		assertThat(this.messageWithArgumentController.messageArgument).isNotNull();
+	}
+
+	@Test
+	public void sendWhenUsingCustomPathMatcherThenSecurityAppliesIt() {
+		this.spring.configLocations(xml("CustomPathMatcherConfig")).autowire();
+
+		Message<?> message = message("/denyAll.a");
+
+		assertThatThrownBy(send(message)).hasCauseInstanceOf(AccessDeniedException.class);
+
+		message = message("/denyAll.a.b");
+
+		assertThatCode(send(message)).doesNotThrowAnyException();
+	}
+
+	@Test
+	public void sendWhenIdSpecifiedThenSecurityDoesNotIntegrateWithClientInboundChannel() {
+		this.spring.configLocations(xml("IdConfig")).autowire();
+
+		Message<?> message = message("/denyAll");
+
+		assertThatCode(send(message)).doesNotThrowAnyException();
+	}
+
+	@Test
+	@WithMockUser
+	public void sendWhenIdSpecifiedAndExplicitlyIntegratedWhenBrokerUsesClientInboundChannel() {
+		this.spring.configLocations(xml("IdIntegratedConfig")).autowire();
+
+		Message<?> message = message("/denyAll");
+
+		assertThatThrownBy(send(message)).hasCauseInstanceOf(AccessDeniedException.class);
+	}
+
+	@Test
+	public void sendWhenNoIdSpecifiedThenSecurityDoesntOverrideCustomInterceptors() {
+		this.spring.configLocations(xml("CustomInterceptorConfig")).autowire();
+
+		Message<?> message = message("/throwAll");
+
+		assertThatThrownBy(send(message)).hasCauseInstanceOf(UnsupportedOperationException.class);
+	}
+
+	@Test
+	@WithMockUser(username = "nile")
+	public void sendWhenCustomExpressionHandlerThenAuthorizesAccordingly() {
+		this.spring.configLocations(xml("CustomExpressionHandlerConfig")).autowire();
+
+		Message<?> message = message("/denyNile");
+
+		assertThatThrownBy(send(message)).hasCauseInstanceOf(AccessDeniedException.class);
+	}
+
+	private String xml(String configName) {
+		return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml";
+	}
+
+	private ThrowableAssert.ThrowingCallable send(Message<?> message) {
+		return () -> this.clientInboundChannel.send(message);
+	}
+
+	private Message<?> message(String destination) {
+		return message(destination, SimpMessageType.MESSAGE);
+	}
+
+	private Message<?> message(String destination, SimpMessageType type) {
+		SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(type);
+		return message(destination, headers);
+	}
+
+	private Message<?> message(String destination, SimpMessageHeaderAccessor headers) {
+		headers.setSessionId("123");
+		headers.setSessionAttributes(new HashMap<>());
+		headers.setDestination(destination);
+
+		if (SecurityContextHolder.getContext().getAuthentication() != null) {
+			headers.setUser(SecurityContextHolder.getContext().getAuthentication());
+		}
+
+		headers.getSessionAttributes().put(CsrfToken.class.getName(), this.token);
+
+		return new GenericMessage<>("hi", headers.getMessageHeaders());
+	}
+
+	@Controller
+	static class MessageController {
+		String username;
+
+		@MessageMapping("/message")
+		public void authentication(@AuthenticationPrincipal String username) {
+			this.username = username;
+		}
+	}
+
+	@Controller
+	static class MessageWithArgumentController {
+		MessageArgument messageArgument;
+
+		@MessageMapping("/message-with-argument")
+		public void myCustom(MessageArgument messageArgument) {
+			this.messageArgument = messageArgument;
+		}
+	}
+
+
+	static class MessageArgument {
+		MessageArgument(String notDefaultConstructor) {
+		}
+	}
+
+	static class MessageArgumentResolver implements HandlerMethodArgumentResolver {
+
+		@Override
+		public boolean supportsParameter(MethodParameter parameter) {
+			return parameter.getParameterType().isAssignableFrom(MessageArgument.class);
+		}
+
+		@Override
+		public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
+			return new MessageArgument("");
+		}
+	}
+
+	static class TestHandshakeHandler implements HandshakeHandler {
+		Map<String, Object> attributes;
+
+		@Override
+		public boolean doHandshake(
+				ServerHttpRequest request,
+				org.springframework.http.server.ServerHttpResponse response,
+				WebSocketHandler wsHandler,
+				Map<String, Object> attributes) throws HandshakeFailureException {
+
+			this.attributes = attributes;
+
+			return true;
+		}
+	}
+
+	static class InboundExecutorPostProcessor implements BeanDefinitionRegistryPostProcessor {
+
+		@Override
+		public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
+			BeanDefinition inbound = registry.getBeanDefinition("clientInboundChannel");
+			inbound.getConstructorArgumentValues()
+					.addIndexedArgumentValue(0, new RootBeanDefinition(SyncTaskExecutor.class));
+		}
+
+		@Override
+		public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+
+		}
+	}
+
+	static class ExceptingInterceptor extends ChannelInterceptorAdapter {
+
+		@Override
+		public Message<?> preSend(Message<?> message, MessageChannel channel) {
+			throw new UnsupportedOperationException("no");
+		}
+	}
+
+	static class DenyNileMessageSecurityExpressionHandler
+			extends DefaultMessageSecurityExpressionHandler<Object> {
+
+		@Override
+		protected SecurityExpressionOperations createSecurityExpressionRoot(
+				Authentication authentication,
+				Message<Object> invocation) {
+
+			return new MessageSecurityExpressionRoot(authentication, invocation) {
+				public boolean denyNile() {
+					Authentication auth = getAuthentication();
+					return auth != null && !"nile".equals(auth.getName());
+				}
+			};
+		}
+	}
+}

+ 31 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-ConnectAckInterceptTypeConfig.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/permitAll" type="CONNECT_ACK" access="permitAll"/>
+		<intercept-message pattern="/**" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 31 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-ConnectInterceptTypeConfig.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/permitAll" type="CONNECT" access="permitAll"/>
+		<intercept-message pattern="/**" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 33 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomExpressionHandlerConfig.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<b:bean name="expressionHandler" class="org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.DenyNileMessageSecurityExpressionHandler"/>
+
+	<websocket-message-broker>
+		<expression-handler ref="expressionHandler"/>
+		<intercept-message pattern="/**" access="denyNile()"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 45 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomInterceptorConfig.xml

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:websocket="http://www.springframework.org/schema/websocket"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+		http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
+
+	<websocket:message-broker application-destination-prefix="/app" user-destination-prefix="/user">
+		<websocket:transport/>
+		<websocket:stomp-endpoint path="/foo">
+			<websocket:sockjs/>
+		</websocket:stomp-endpoint>
+		<websocket:simple-broker prefix="/queue, /topic"/>
+		<websocket:client-inbound-channel>
+			<websocket:interceptors>
+				<b:ref bean="eci"/>
+			</websocket:interceptors>
+		</websocket:client-inbound-channel>
+	</websocket:message-broker>
+
+	<b:bean name="eci" class="org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.ExceptingInterceptor"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/permitAll" access="permitAll"/>
+		<intercept-message pattern="/denyAll" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 46 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomPathMatcherConfig.xml

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:websocket="http://www.springframework.org/schema/websocket"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+		http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+
+	<websocket:message-broker path-matcher="pathMatcher">
+		<websocket:transport/>
+		<websocket:stomp-endpoint path="/app">
+			<websocket:handshake-handler ref="testHandler"/>
+		</websocket:stomp-endpoint>
+
+		<websocket:simple-broker prefix="/queue, /topic"/>
+	</websocket:message-broker>
+
+	<b:bean name="pathMatcher" class="org.springframework.util.AntPathMatcher">
+		<b:constructor-arg value="."/>
+	</b:bean>
+
+	<b:bean name="testHandler" class="org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.TestHandshakeHandler"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/denyAll.*" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 31 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-DisconnectAckInterceptTypeConfig.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/permitAll" type="DISCONNECT_ACK" access="permitAll"/>
+		<intercept-message pattern="/**" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 31 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-DisconnectInterceptTypeConfig.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/permitAll" type="HEARTBEAT" access="permitAll"/>
+		<intercept-message pattern="/**" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 31 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-HeartbeatInterceptTypeConfig.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/permitAll" type="HEARTBEAT" access="permitAll"/>
+		<intercept-message pattern="/**" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 30 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-IdConfig.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<websocket-message-broker id="inCsi">
+		<intercept-message pattern="/**" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 42 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-IdIntegratedConfig.xml

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:websocket="http://www.springframework.org/schema/websocket"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+		http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
+
+	<websocket:message-broker>
+		<websocket:transport/>
+		<websocket:stomp-endpoint path="/app">
+			<websocket:sockjs/>
+		</websocket:stomp-endpoint>
+		<websocket:simple-broker prefix="/queue, /topic"/>
+		<websocket:client-inbound-channel>
+			<websocket:interceptors>
+				<b:ref bean="inCsi"/>
+			</websocket:interceptors>
+		</websocket:client-inbound-channel>
+	</websocket:message-broker>
+
+	<websocket-message-broker id="inCsi">
+		<intercept-message pattern="/**" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 31 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-MessageInterceptTypeConfig.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/permitAll" type="MESSAGE" access="permitAll"/>
+		<intercept-message pattern="/**" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 31 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-NoIdConfig.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/permitAll" access="permitAll"/>
+		<intercept-message pattern="/denyAll" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 31 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-OtherInterceptTypeConfig.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/permitAll" type="OTHER" access="permitAll"/>
+		<intercept-message pattern="/**" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 31 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SubscribeInterceptTypeConfig.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/permitAll" type="SUBSCRIBE" access="permitAll"/>
+		<intercept-message pattern="/**" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 31 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SyncConfig.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/sync.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/**" access="permitAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 50 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SyncCustomArgumentResolverConfig.xml

@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		xmlns:websocket="http://www.springframework.org/schema/websocket"
+		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xmlns="http://www.springframework.org/schema/security"
+		xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+		http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/sync.xml"/>
+
+	<websocket:message-broker>
+		<websocket:transport/>
+		<websocket:stomp-endpoint path="/app">
+			<websocket:handshake-handler ref="testHandler"/>
+			<websocket:handshake-interceptors>
+				<b:bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
+			</websocket:handshake-interceptors>
+		</websocket:stomp-endpoint>
+
+		<websocket:simple-broker prefix="/queue, /topic"/>
+		<websocket:argument-resolvers>
+			<b:ref bean="messageArgumentResolver"/>
+		</websocket:argument-resolvers>
+	</websocket:message-broker>
+
+	<b:bean name="testHandler" class="org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.TestHandshakeHandler"/>
+	<b:bean name="messageArgumentResolver" class="org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.MessageArgumentResolver"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/**" access="permitAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 31 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SyncSameOriginDisabledConfig.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/sync.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<websocket-message-broker same-origin-disabled="true">
+		<intercept-message pattern="/**" access="permitAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 31 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SyncSockJsConfig.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/sync.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket-sockjs.xml"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/**" access="permitAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 31 - 0
config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-UnsubscribeInterceptTypeConfig.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<b:import resource="classpath:org/springframework/security/config/websocket/controllers.xml"/>
+	<b:import resource="classpath:org/springframework/security/config/websocket/websocket.xml"/>
+
+	<websocket-message-broker>
+		<intercept-message pattern="/permitAll" type="UNSUBSCRIBE" access="permitAll"/>
+		<intercept-message pattern="/**" access="denyAll"/>
+	</websocket-message-broker>
+
+</b:beans>

+ 24 - 0
config/src/test/resources/org/springframework/security/config/websocket/controllers.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xmlns="http://www.springframework.org/schema/beans"
+		xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<bean class="org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.MessageController"/>
+	<bean class="org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.MessageWithArgumentController"/>
+
+</beans>

+ 22 - 0
config/src/test/resources/org/springframework/security/config/websocket/sync.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xmlns="http://www.springframework.org/schema/beans"
+		xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<bean class="org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.InboundExecutorPostProcessor"/>
+</beans>

+ 37 - 0
config/src/test/resources/org/springframework/security/config/websocket/websocket-sockjs.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xmlns="http://www.springframework.org/schema/websocket"
+		 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+		http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
+
+	<message-broker>
+		<transport/>
+		<stomp-endpoint path="/app">
+			<handshake-handler ref="testHandler"/>
+			<handshake-interceptors>
+				<b:bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
+			</handshake-interceptors>
+			<sockjs/>
+		</stomp-endpoint>
+
+		<simple-broker prefix="/queue, /topic"/>
+	</message-broker>
+
+	<b:bean name="testHandler" class="org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.TestHandshakeHandler"/>
+</b:beans>

+ 36 - 0
config/src/test/resources/org/springframework/security/config/websocket/websocket.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xmlns="http://www.springframework.org/schema/websocket"
+		 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+		http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
+
+	<message-broker>
+		<transport/>
+		<stomp-endpoint path="/app">
+			<handshake-handler ref="testHandler"/>
+			<handshake-interceptors>
+				<b:bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
+			</handshake-interceptors>
+		</stomp-endpoint>
+
+		<simple-broker prefix="/queue, /topic"/>
+	</message-broker>
+
+	<b:bean name="testHandler" class="org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.TestHandshakeHandler"/>
+</b:beans>