Closed caddac closed 7 years ago
grails 2.4.5 is using spring 4.0.9. looks like setHandshakeHandler
is already supported in that version: https://github.com/spring-projects/spring-framework/blob/v4.0.9.RELEASE/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/StompWebSocketEndpointRegistration.java#L37
so registering a custom handshakeHandler, possibly overriding determineUser
(https://github.com/spring-projects/spring-framework/blob/v4.0.9.RELEASE/spring-websocket/src/main/java/org/springframework/web/socket/server/support/DefaultHandshakeHandler.java#L348) should work.
without a custom handshake handler, relying on the default principal lookup of DefaultHandshakeHandler
, another way would be to add a servlet filter into your filterchain that wraps the request in a HttpServletRequestWrapper
and overrides getUserPrincipal()
to return a principal that has a name
corresponding to your cookie-authenticated username/userid.
spring security does that, too: https://github.com/spring-projects/spring-security/blob/4.0.0.RELEASE/web/src/main/java/org/springframework/security/web/servletapi/SecurityContextHolderAwareRequestWrapper.java#L140
OK, I tried implementing the solution given in #35 but am getting the error:
2017-01-03 14:37:06,292 [localhost-startStop-1] ERROR context.GrailsContextLoaderListener - Error initializing the application: Error creating bean with name 'grailsSimpAnnotationMethodMessageHandler': Cannot resolve reference to bean 'clientInboundChannel' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration.setConfigurers(java.util.List); nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webSocketConfig': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [websocket.WebSocketConfig$$EnhancerBySpringCGLIB$$993b4422]: No default constructor found; nested exception is java.lang.NoSuchMethodException: websocket.WebSocketConfig$$EnhancerBySpringCGLIB$$993b4422.
you followed the 1.2.x readme? i.e. grails.plugin.springwebsocket.useCustomConfig = true
?
and what does your websocketConfig look like? how is it registered? resources.groovy
?
org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [websocket.WebSocketConfig$$EnhancerBySpringCGLIB$$993b4422]: No default constructor found;
looks like component-scan.
can you double-check that the websocketConfig you defined now in resource.groovy
is not subject to component-scan (e.g. in a package that is not included in spring.bean.packages
)?
yes, followed the 1.2x readme including the useCustomConfig
.
src\groovy\websocket\WebSocketConfig.groovy
package websocket
import org.h2.engine.User
import org.springframework.context.annotation.Configuration
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServletServerHttpRequest
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry
import org.springframework.web.socket.server.support.DefaultHandshakeHandler
import javax.servlet.http.HttpSession
import java.security.Principal;
/**
* Set up our websocket configuration, which uses STOMP, and configure our endpoints
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
ConfigObject config
/**
* using constructor-based injection here because the overridden configuration methods
* are called (it seems) before property injection or @PostConstruct handling take place
*/
WebSocketConfig(ConfigObject config) {
assert config
this.config = config
}
public class HandshakeHandler extends DefaultHandshakeHandler {
@Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
Principal principal = request.getPrincipal();
if(principal==null)
{
ServletServerHttpRequest req= (ServletServerHttpRequest)request;
HttpSession session = req.getServletRequest().getSession();
final User user = (User) session.getAttribute("user");
principal=new Principal() {
@Override
public String getName() {
return user!=null?user.getId():"";
}
};
}
logger.info("principal:{}",principal);
return principal;
}
}
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/queue", "/topic");
config.setApplicationDestinationPrefixes("/app");
config.setUserDestinationPrefix "/user/"
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/stomp").setHandshakeHandler(new HandshakeHandler()).withSockJS();
}
}
resources.groovy
beans = {
//other bean registrations removed for brevity
webSocketConfig websocket.WebSocketConfig
grailsSimpAnnotationMethodMessageHandler(
GrailsSimpAnnotationMethodMessageHandler,
ref("clientInboundChannel"), //this is what seems to be causing the error, should this be some other value?
ref("clientOutboundChannel"),
ref("brokerMessagingTemplate")
) {
destinationPrefixes = ["/app"]
}
oh, please try to remove the constructor from the websocket config. that was only for the plugins default config.
if you write webSocketConfig websocket.WebSocketConfig
in resources.groovy
, spring does of course try to invoke the default constructor (that is missing).
so, no websocket channels were created because websocket config instantiation failed.
but those channels are required for the GrailsSimpAnnotationMethodMessageHandler, ofc.
Adding a default constructor to WebsocketsConfig fixed it.
WebSocketConfig(){}
Thanks for all your help. I really appreciate it.
I have a Grails 2.4 project using the 1.2x branch of this plugin. I'm using a custom cookie based authentication scheme (not spring security) but I do get the users details in the grails session. I'm trying to understand the best way to send a message directly to a user. I've tried registering a custom handshake handler as described in #35 but haven't had any luck getting that to work. It fails when trying to register the handler.
Does this library support this feature for grails 2.4?