mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-27 06:12:27 +00:00
SEC-2864: Default Spring Security WebSocket PathMatcher XML Namespace
This commit is contained in:
parent
db531d9100
commit
7b25b3e40d
@ -38,11 +38,14 @@ import org.springframework.security.messaging.util.matcher.SimpMessageTypeMatche
|
||||
import org.springframework.security.messaging.web.csrf.CsrfChannelInterceptor;
|
||||
import org.springframework.security.messaging.web.socket.server.CsrfTokenHandshakeInterceptor;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Parses Spring Security's websocket namespace support. A simple example is:
|
||||
@ -84,9 +87,6 @@ import java.util.List;
|
||||
*/
|
||||
public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
||||
BeanDefinitionParser {
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(WebSocketMessageBrokerSecurityBeanDefinitionParser.class);
|
||||
|
||||
private static final String ID_ATTR = "id";
|
||||
|
||||
private static final String DISABLED_ATTR = "same-origin-disabled";
|
||||
@ -97,6 +97,8 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
||||
|
||||
private static final String TYPE_ATTR = "type";
|
||||
|
||||
private static final String PATH_MATCHER_BEAN_NAME = "springSecurityMessagePathMatcher";
|
||||
|
||||
/**
|
||||
* @param element
|
||||
* @param parserContext
|
||||
@ -149,6 +151,10 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
||||
|
||||
if (StringUtils.hasText(id)) {
|
||||
registry.registerAlias(inSecurityInterceptorName, id);
|
||||
|
||||
if(!registry.containsBeanDefinition(PATH_MATCHER_BEAN_NAME)) {
|
||||
registry.registerBeanDefinition(PATH_MATCHER_BEAN_NAME, new RootBeanDefinition(AntPathMatcher.class));
|
||||
}
|
||||
}
|
||||
else {
|
||||
BeanDefinitionBuilder mspp = BeanDefinitionBuilder
|
||||
@ -190,16 +196,18 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
||||
interceptMessage);
|
||||
}
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder matcher = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(SimpDestinationMessageMatcher.class);
|
||||
matcher.setFactoryMethod(factoryName);
|
||||
matcher.addConstructorArgValue(matcherPattern);
|
||||
matcher.addConstructorArgValue(new RootBeanDefinition(AntPathMatcher.class));
|
||||
matcher.addConstructorArgValue(new RuntimeBeanReference("springSecurityMessagePathMatcher"));
|
||||
return matcher.getBeanDefinition();
|
||||
}
|
||||
|
||||
static class MessageSecurityPostProcessor implements
|
||||
BeanDefinitionRegistryPostProcessor {
|
||||
|
||||
private static final String CLIENT_INBOUND_CHANNEL_BEAN_ID = "clientInboundChannel";
|
||||
|
||||
private static final String INTERCEPTORS_PROP = "interceptors";
|
||||
@ -233,6 +241,14 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
||||
argResolvers.add(new RootBeanDefinition(
|
||||
AuthenticationPrincipalArgumentResolver.class));
|
||||
bd.getPropertyValues().add(CUSTOM_ARG_RESOLVERS_PROP, argResolvers);
|
||||
|
||||
if(!registry.containsBeanDefinition(PATH_MATCHER_BEAN_NAME)) {
|
||||
PropertyValue pathMatcherProp = bd.getPropertyValues().getPropertyValue("pathMatcher");
|
||||
Object pathMatcher = pathMatcherProp == null ? null : pathMatcherProp.getValue();
|
||||
if(pathMatcher instanceof BeanReference) {
|
||||
registry.registerAlias(((BeanReference) pathMatcher).getBeanName(), PATH_MATCHER_BEAN_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (beanClassName
|
||||
.equals("org.springframework.web.socket.server.support.WebSocketHttpRequestHandler")) {
|
||||
@ -270,6 +286,10 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
||||
}
|
||||
|
||||
inboundChannel.getPropertyValues().add(INTERCEPTORS_PROP, interceptors);
|
||||
|
||||
if(!registry.containsBeanDefinition(PATH_MATCHER_BEAN_NAME)) {
|
||||
registry.registerBeanDefinition(PATH_MATCHER_BEAN_NAME, new RootBeanDefinition(AntPathMatcher.class));
|
||||
}
|
||||
}
|
||||
|
||||
private void addCsrfTokenHandshakeInterceptor(BeanDefinition bd) {
|
||||
@ -289,4 +309,41 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static class DelegatingPathMatcher implements PathMatcher {
|
||||
|
||||
private PathMatcher delegate = new AntPathMatcher();
|
||||
|
||||
public boolean isPattern(String path) {
|
||||
return delegate.isPattern(path);
|
||||
}
|
||||
|
||||
public boolean match(String pattern, String path) {
|
||||
return delegate.match(pattern, path);
|
||||
}
|
||||
|
||||
public boolean matchStart(String pattern, String path) {
|
||||
return delegate.matchStart(pattern, path);
|
||||
}
|
||||
|
||||
public String extractPathWithinPattern(String pattern, String path) {
|
||||
return delegate.extractPathWithinPattern(pattern, path);
|
||||
}
|
||||
|
||||
public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
|
||||
return delegate.extractUriTemplateVariables(pattern, path);
|
||||
}
|
||||
|
||||
public Comparator<String> getPatternComparator(String path) {
|
||||
return delegate.getPatternComparator(path);
|
||||
}
|
||||
|
||||
public String combine(String pattern1, String pattern2) {
|
||||
return delegate.combine(pattern1, pattern2);
|
||||
}
|
||||
|
||||
void setPathMatcher(PathMatcher pathMatcher) {
|
||||
this.delegate = pathMatcher;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
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
|
||||
@ -11,21 +13,29 @@ 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.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.messaging.util.matcher.SimpMessageTypeMatcher
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.web.csrf.CsrfToken
|
||||
import org.springframework.security.web.csrf.DefaultCsrfToken
|
||||
import org.springframework.security.web.csrf.InvalidCsrfTokenException
|
||||
import org.springframework.security.web.csrf.MissingCsrfTokenException
|
||||
import org.springframework.stereotype.Controller
|
||||
import org.springframework.util.AntPathMatcher
|
||||
import org.springframework.web.servlet.HandlerMapping
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
|
||||
import org.springframework.web.socket.WebSocketHandler
|
||||
import org.springframework.web.socket.server.HandshakeFailureException
|
||||
import org.springframework.web.socket.server.HandshakeHandler
|
||||
@ -33,20 +43,9 @@ import org.springframework.web.socket.server.support.HttpSessionHandshakeInterce
|
||||
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
|
||||
|
||||
import static org.mockito.Mockito.*
|
||||
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.messaging.MessageDeliveryException
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor
|
||||
import org.springframework.messaging.support.ChannelInterceptor
|
||||
import org.springframework.messaging.support.GenericMessage
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||
import org.springframework.security.config.AbstractXmlConfigTests
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
@ -309,6 +308,37 @@ class WebSocketMessageBrokerConfigTests extends AbstractXmlConfigTests {
|
||||
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']) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user