pacman
pacman

Reputation: 835

SimpUserRegistry doesnot contain any session objects

Iam new to Websockets. I have been trying to use SimpUserRegistry to find session object by Principal. I wrote a custom handshake handler to convert Anonymous users to authenticated users and Iam able to access the Principal name from Websocket session object.

The code for custom handshake handler is shown below


import java.security.Principal;

public class StompPrincipal implements Principal {
    private String name;

    public StompPrincipal(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }
}

Handler

class CustomHandshakeHandlerTwo extends DefaultHandshakeHandler {
    // Custom class for storing principal
    @Override
    protected Principal determineUser(
            ServerHttpRequest request,
            WebSocketHandler wsHandler,
            Map<String, Object> attributes
    ) {
        // Generate principal with UUID as name
        return new StompPrincipal(UUID.randomUUID().toString());
    }
}

But as specified in many questions like this I'am not able to inject the SimpUserRegistry directly.

It throws error


Field simpUserRegistry  required a bean of type 'org.springframework.messaging.simp.user.SimpUserRegistry' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'org.springframework.messaging.simp.user.SimpUserRegistry' in your configuration.


So I created a configuration class as shown below.


@Configuration
public class UsersConfig {
    final private SimpUserRegistry userRegistry = new DefaultSimpUserRegistry();

    @Bean
    @Primary
    public SimpUserRegistry userRegistry() {
        return userRegistry;
    }
}

Now I can autowire and use it but everytime I try to acess the SimpUserRegistry it is empty.

What could be the cause of this problem?

EDIT:

Showing websocket config

@Configuration
@EnableWebSocket
@Controller
@Slf4j
public class WebSocketConfig implements WebSocketConfigurer {
    
    @Autowired
    EventTextHandler2 handler;
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        log.info("Registering websocket handler SocketTextHandler");
        registry.addHandler(handler, "/event").setHandshakeHandler(new CustomHandshakeHandlerTwo());
    }
}

Upvotes: 2

Views: 1857

Answers (1)

Florian Lopes
Florian Lopes

Reputation: 1289

SimpUserRegistry is an "infrastructure bean" registered/provided by Spring WebSocket, you should not instantiate it directly.

Is your WebSocket Spring configuration correct?

  1. Make sure your application is well configured (ie. your configuration class is being scanned).

SimpUserRegistry is imported by spring-messaging dependency: make sure your configuration class is annotated with @EnableWebSocketMessageBroker.

Official documentation: https://docs.spring.io/spring-framework/docs/5.3.6/reference/html/web.html#websocket-stomp-enable

  1. To back the connected users in Redis, you may want to create a new SimpUserRegistry implementation:
public class RedisSimpUserRegistry implements SimpUserRegistry, SmartApplicationListener {

  private final RedisTemplate redisTemplate;

  public RedisSimpUserRegistry(RedisTemplate redisTemplate) {
    this.redisTemplate = redisTemplate;
  }

  [...]

  @Override
  public void onApplicationEvent(ApplicationEvent event) {
    // Maintain Redis collection on event type
    // ie. SessionConnectedEvent / SessionDisconnectEvent
  }
  [...]
}

PS: The @Controller annotation on your config class is not necessary unless you have an endpoint defined in it.

Edit after new comments:

You can see the DefaultSimpUserRegistry implementation to get an idea of how to do it.

To intercept an application event, you have to implement the ApplicationListener interface (in this case SmartApplicationListener). The supportsEventType method is important to define which event types you want to intercept:

  @Override
  public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
    return AbstractSubProtocolEvent.class.isAssignableFrom(eventType);
  }

The AbstractSubProtocolEvent have multiple implementations. The most important ones are SessionConnectEvent, SessionDisconnectEvent.

Intercepting (see onApplicationEvent method) these event types will allow your implementation to maintain the desired state in your Redis cache. You could then store users (ids, etc.).

Upvotes: 3

Related Questions