|
WebSpider member offline |
|
posts: |
147 |
joined: |
06/29/2006 |
from: |
Seattle, WA |
|
|
|
|
|
WebSocket |
WebSocket is a protocol to provide bidirectional communication channels over a single TCP connection. It is vital to build the highly interactive real-time web applications like games, chats and bots.
Dependencies: spring-websocket -- from org.springframework for WebSocket spring-messaging -- from org.springframework for messaging jackson-core -- from com.fasterxml.jackson.core for JSON message jackson-databind -- from com.fasterxml.jackson.core for JSON message
|
|
|
|
|
|
|
WebSpider member offline |
|
posts: |
147 |
joined: |
06/29/2006 |
from: |
Seattle, WA |
|
|
|
|
|
Configuration |
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", "/queue");
config.setApplicationDestinationPrefixes("/channel");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/stomp").withSockJS();
}
}
As the annoation @EnableWebSocketMessageBroker suggests, it will enable MESSAGE handling backed by a broker. Here: ".enableSimpleBroker" -- the simple built-in IN-MEMORY (Simple) message broker (Kafka can be a broker for production); "/topic" -- the destination for subscribers -- a folder holding multiple sub-topics "/channel" -- the destination for publishers -- a folder holding multiple sub-channels "/stomp" -- the STOMP's endpoint for connection (STOMP: Simple Text-Orientated Messaging Protocol) ".withSockJS()" -- with SockJS as fallback if WebSocket is not supported by browser.
|
|
|
|
|
|
|
WebSpider member offline |
|
posts: |
147 |
joined: |
06/29/2006 |
from: |
Seattle, WA |
|
|
|
|
|
How it works -- how the broker wire the publish-subscribe mapping internally? |
@Controller
public class WebSocketController {
// publisher send message to --> "/channel/chat"
// --> broker relay it to --> "/topic/messages"
// --> broker push it to all subscribers
@MessageMapping("/chat")
@SendTo("/topic/messages")
public OutputMessage send(Message message) throws Exception {
String time = new SimpleDateFormat("HH:mm").format(new Date());
return new OutputMessage(message.getFrom(), message.getText(), time);
}
}
|
|
|
|
|
|
|
WebSpider member offline |
|
posts: |
147 |
joined: |
06/29/2006 |
from: |
Seattle, WA |
|
|
|
|
|
Example -- JS Client |
Connect & Subscribe
function connect() {
// 'websocket' -- naming context
// 'api' -- servlet context
// 'stomp' -- STOMP endpoint
var socket = new SockJS('/websocket/api/stomp');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
stompClient.subscribe('/topic/messages',
function(messageOutput) { // JS Object <-- Java OutputMessage
showMessageOutput(JSON.parse(messageOutput.body)); // JS String --> JS Object
}
);
});
}
Publish
function sendMessage() {
stompClient.send('/channel/chat',
{}, // header
JSON.stringify({...}) // JS Object --> JS String
);
}
|
|
|
|
|
|
|
WebSpider member offline |
|
posts: |
147 |
joined: |
06/29/2006 |
from: |
Seattle, WA |
|
|
|
|
|
WebSocket Security |
Once your messaging system is on, you definitely have to regulate as to: who can connect to STOMP ? who can send message to /channel as publisher? who can receive message from /topic as subscriber ?
This is the place where WebSocket Security come into play.
Prerequisite:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-messaging</artifactId>
</dependency>
Config:
@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
// Messages with destination: MESSAGE, SUBSCRIBE
// Messages w/o destination: CONNECT, DISCONNECT, UNSUBSCRIBE
// nullDestMatcher() -- CONNECT, DISCONNECT, UNSUBSCRIBE
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
// ######## connect #################
.nullDestMatcher()
.authenticated() // CONNECT, DISCONNECT, UNSUBSCRIBE needs authenticated
// ######## send #################
.simpDestMatchers("/channel/**")
.hasRole("USER") // any sessage SENT to "/channel/" will require ROLE_USER
// ######## receive #################
.simpSubscribeDestMatchers("/topic/**", "/queue/*")
.hasRole("ADMIN") // any message SUBCRIBED for "/topic/ or /queue/" will require ROLE_ADMIN
.anyMessage().denyAll();
}
@Override
protected boolean sameOriginDisabled() {
return true;
}
}
|
|
|
|
|
|
|
|