I have a working spring multi tenant application. It uses the familiar construct of a thread local variable and a web filter, and uses the hostname (subdomain) to determine which tenant is active.
But the browser also opens a websocket and sends messages. Somehow I need to do the same trick there; I need to match an incoming message to a tenant, aka the hostname that was used to start the websocket. There is no concept of a filter for websockets.
There is a HandshakeInterceptor in which the hostname can be accessed, or maybe via a WebSocketEventListener, so something probably can be constructed there. But I cannot seem to find a way link an incoming message (in a @MessageMapping method of a @Controller class) to anything sensible.
Any insights in how to do that?
It took some digging, but for people ending up with the same question. Put the code that determines the tenantId from a HttpServletRequest and the code that configures any logging (e.g. MDC) already present in de TenantFilter in reusable static methods. And then:
.addInterceptors(new HandshakeInterceptor() {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
// Determine the tenantId (see TenantFilter)
ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) request;
HttpServletRequest httpServletRequest = servletServerHttpRequest.getServletRequest();
String tenantId = TenantFilter.determineTenantId(httpServletRequest, ...);
attributes.put("tenantId", tenantId);
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
}
})
@MessageMapping("/SetNoteworthy")
public void browserToBackend(SetNoteworthy command, @Header("simpSessionId") String sessionId, @Header("simpSessionAttributes") Map<String, String> attributes) throws Exception {
String tenantId = attributes.get("tenantId");
if (LOG.isInfoEnabled()) LOG.info(BROWSER_TO_BACKEND + command);
TenantFilter.setCurrentTenant(tenantId, ...);
}