I am using SpringBoot 2.0 (Spring 5.0.4) with SpringSecurity and STOMP (Websockets). My connect endpoint is protected, and Authorization using @PreAuthorize on all of my standard REST endpoints works correctly. However, when I add @PreAuthorize to my STOMP message handlers I get the exception below.
I have a channel interceptor, and in the preSend method,I can see that the user principal is there on the StompHeaderAccessor.
It seems that something failed to initialize the SecurityContext... Any ideas on where this should be intialized?
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:379)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:223)
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:65)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at com.vertafore.ws.controller.EventStompControllerV1$$EnhancerBySpringCGLIB$$33f954c1.processMessageFromClient(<generated>)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:181)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:114)
at org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMatch(AbstractMethodMessageHandler.java:517)
at org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler.handleMatch(SimpAnnotationMethodMessageHandler.java:495)
at org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler.handleMatch(SimpAnnotationMethodMessageHandler.java:88)
at org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMessageInternal(AbstractMethodMessageHandler.java:475)
at org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMessage(AbstractMethodMessageHandler.java:411)
at org.springframework.messaging.support.ExecutorSubscribableChannel$SendTask.run(ExecutorSubscribableChannel.java:138)
at org.springframework.cloud.sleuth.instrument.async.TraceRunnable.run(TraceRunnable.java:62)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.base/java.lang.Thread.run(Thread.java:844)
This solved it in my case. Note, I am using spring-security 5.
The relevant line I was missing was messages.simpDestMatchers("/live/**").authenticated();. Without that, I ran into the same Exception.
Also, sameOriginDisabled is to disable CSRF if you have this problem aswell.
@Configuration
@EnableWebSocketMessageBroker
public class RegaDAWSWebsocketConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
@Value("${websocket.heartbeatInterval}")
private int heartbeatInterval;
@Value("${websocket.useSockJS}")
private boolean useSockJS;
@Value("${websocket.allowedOrigins}")
private String allowedOrigins;
@Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
config.enableSimpleBroker("/");
config.setApplicationDestinationPrefixes("/");
}
@Override
public void registerStompEndpoints(@NotNull final StompEndpointRegistry registry) {
// Allowing both plain Websocket and SockJS
if (useSockJS) {
registry.addEndpoint("/live")
.setAllowedOriginPatterns(allowedOrigins)
.withSockJS()
.setHeartbeatTime(heartbeatInterval);
} else {
registry.addEndpoint("/live").setAllowedOriginPatterns(allowedOrigins);
}
}
@Override
protected void configureInbound(final MessageSecurityMetadataSourceRegistry messages) {
messages.simpDestMatchers("/live/**").authenticated();
}
@Override
protected boolean sameOriginDisabled() {
return true;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With