I'm trying to get the the Principal user name from Spring websocket SessionConnectEvent but it is null on every listener. What I can be doing wrong?
To implement it I followed the answers you will find here: how to capture connection event in my webSocket server with Spring 4?
@Slf4j
@Service
public class SessionEventListener {
@EventListener
private void handleSessionConnect(SessionConnectEvent event) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(event.getMessage());
String sessionId = headers.getSessionId();
log.debug("sessionId is " + sessionId);
String username = headers.getUser().getName(); // headers.getUser() is null
log.debug("username is " + username);
}
@EventListener
private void handleSessionConnected(SessionConnectEvent event) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(event.getMessage());
String sessionId = headers.getSessionId();
log.debug("sessionId is " + sessionId);
String username = headers.getUser().getName(); // headers.getUser() is null
log.debug("username is " + username);
}
@EventListener
private void handleSubscribeEvent(SessionSubscribeEvent event) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(event.getMessage());
String sessionId = headers.getSessionId();
log.debug("sessionId is " + sessionId);
String subscriptionId = headers.getSubscriptionId();
log.debug("subscriptionId is " + subscriptionId);
String username = headers.getUser().getName(); // headers.getUser() is null
log.debug("username is " + username);
}
@EventListener
private void handleUnsubscribeEvent(SessionUnsubscribeEvent event) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(event.getMessage());
String sessionId = headers.getSessionId();
log.debug("sessionId is " + sessionId);
String subscriptionId = headers.getSubscriptionId();
log.debug("subscriptionId is " + subscriptionId);
String username = headers.getUser().getName(); // headers.getUser() is null
log.debug("username is " + username);
}
@EventListener
private void handleSessionDisconnect(SessionDisconnectEvent event) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(event.getMessage());
log.debug("sessionId is " + event.getSessionId());
String username = headers.getUser().getName(); // headers.getUser() is null
log.debug("username is " + username);
}
}
This is my security config:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.permitAll()
.and().csrf().disable();
}
}
As I'm not implementing an authentication mechanisim, Spring has no enough information to provide a Principal user name. So what I had to do is to configure a HandshakeHandler that generates the Principal.
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
public static final String ENDPOINT_CONNECT = "/connect";
public static final String SUBSCRIBE_USER_PREFIX = "/private";
public static final String SUBSCRIBE_USER_REPLY = "/reply";
public static final String SUBSCRIBE_QUEUE = "/queue";
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker(SUBSCRIBE_QUEUE, SUBSCRIBE_USER_REPLY);
registry.setUserDestinationPrefix(SUBSCRIBE_USER_PREFIX);
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint(ENDPOINT_CONNECT)
// assign a random username as principal for each websocket client
// this is needed to be able to communicate with a specific client
.setHandshakeHandler(new AssignPrincipalHandshakeHandler())
.setAllowedOrigins("*");
}
}
/**
* Assign a random username as principal for each websocket client. This is
* needed to be able to communicate with a specific client.
*/
public class AssignPrincipalHandshakeHandler extends DefaultHandshakeHandler {
private static final String ATTR_PRINCIPAL = "__principal__";
@Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
final String name;
if (!attributes.containsKey(ATTR_PRINCIPAL)) {
name = generateRandomUsername();
attributes.put(ATTR_PRINCIPAL, name);
} else {
name = (String) attributes.get(ATTR_PRINCIPAL);
}
return new Principal() {
@Override
public String getName() {
return name;
}
};
}
private String generateRandomUsername() {
RandomStringGenerator randomStringGenerator =
new RandomStringGenerator.Builder()
.withinRange('0', 'z')
.filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS).build();
return randomStringGenerator.generate(32);
}
}