赞
踩
Spring Boot 对 WebSocket 提供了非常友好的支持,可以方便开发者在项目中快速集成 WebSocket 功能,实现单聊或者群聊。
依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator-core</artifactId> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>sockjs-client</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>stomp-websocket</artifactId> <version>2.3.3</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.3.1</version> </dependency>
spring-boot-starter-websocket 依赖是 Web Socket 相关依赖,其它的都是前端库,使用 jar 包的形式对这些前端库进行统一管理,使用 webjars 添加到项目中的前端库,在 Spring Boot 项目中已经默认添加了静态资源过滤,因此可以直接用。
Spring 框架提供了基于 WebSocket 的 STOMP 支持,STOMP 是一个简单的可互操作的协议,通常被用于通过中间服务器在客户端之间进行异步消息传递。WebSocket 配置如下:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS();
}
}
代码解释:
@Controller
public class GreetingController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Message greeting(Message message) throws Exception {
return message;
}
}
@MessageMapping(“/hello”) 注解将用来接收 “/app/hello”路径发来的消息,在注解方法中对消息进行处理后,再将消息转发到 @SendTo 定义的路径上,而 @SendTo 路径前缀是一个前缀为“/topic”的路径,因此该消息将被交给消息代理 broker ,再由 broker 进行广播。
public class Message { private String name; private String content; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
在 resources/static 目录下创建 chat.html 页面作为聊天页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>群聊</title> <script src="/webjars/jquery/jquery.min.js"></script> <script src="/webjars/sockjs-client/sockjs.min.js"></script> <script src="/webjars/stomp-websocket/stomp.min.js"></script> <script src="/app.js"></script> </head> <body> <div> <label for="name">请输入用户名:</label> <input type="text" id="name" placeholder="用户名"> </div> <div> <button id="connect" type="button">连接</button> <button id="disconnect" type="button" disabled="disabled">断开连接</button> </div> <div id="chat" style="display: none;"> <div> <label for="name">请输入聊天内容:</label> <input type="text" id="content" placeholder="聊天内容"> </div> <button id="send" type="button">发送</button> <div id="greetings"> <div id="conversation" style="display: none">群聊进行中...</div> </div> </div> </body> </html>
引入外部的 JS 库 ,这些 JS 库在 pom.xml 文件中通过依赖加入进来。app.js 为自定义 JS ,如下:
var stompClient = null; function setConnected(connected) { $("#connect").prop("disabled", connected); $("#disconnect").prop("disabled", !connected); if (connected) { $("#conversation").show(); $("#chat").show(); } else { $("#conversation").hide(); $("#chat").hide(); } $("#greetings").html(""); } function connect() { if (!$("#name").val()) { return; } var socket = new SockJS('/chat'); stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { setConnected(true); stompClient.subscribe('/topic/greetings', function (greeting) { showGreeting(JSON.parse(greeting.body)); }); }); } function disconnect() { if (stompClient !== null) { stompClient.disconnect(); } setConnected(false); } function sendName() { stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val(),'content':$("#content").val()})); } function showGreeting(message) { $("#greetings") .append("<div>" + message.name+":"+message.content + "</div>"); } $(function () { $( "#connect" ).click(function() { connect(); }); $( "#disconnect" ).click(function() { disconnect(); }); $( "#send" ).click(function() { sendName(); }); });
代码解释:
启动项目,http://localhost:8080/chat.html
输入用户名,然后点击连接按钮
然后换一个浏览器,重复刚才的步骤,这样就有两个用户连接上了,接下来便可开始群聊了
最后连接上的不显示之前的聊天内容
在 11.3.1节 中消息发送用了 @SendTo 注解,该注解将方法处理过的消息转发到 broker ,再由 broker 广播。除了 @SendTo 注解,Spring 还提供了 SimpMessagingTemplate 类来让开发者更加灵活地发送消息,使用 SimpMessagingTemplate 可以对 11.3.1小节的 Controller 进行如下改造
@Controller
public class GreetingController {
@Autowired
SimpMessagingTemplate messagingTemplate;
@MessageMapping("/hello")
public void greeting(Message message) throws Exception {
messagingTemplate.convertAndSend("/topic/greetings",message);
}
}
改造完成直接运行,运行结果与 11.3.1小节的运行结果一致。这里使用 SimpMessagingTemplate 进行消息的发送,在 Spring Boot 中,SimpMessagingTemplate 已经配置好,开发者直接注入进来即可。
使用 SimpMessagingTemplate,开发者可以在任何地方发消息到 broker ,也可以发送消息给某一个用户,这就是点对点的消息发送,在 11.3.1 的基础上实现。
既然是点对点发送,就应该有用户的概念,因此加入 Spring Security 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
对 Spring Security 进行配置,添加两个用户,同时配置所有地址认证后才能访问
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("admin") .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") .roles("admin") .and() .withUser("sang") .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") .roles("user"); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin().permitAll(); } }
相关配置含义参考10.1节《十、Spring Boot 安全管理(1)》
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", "/queue");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS();
}
}
这里的修改是在 config.enableSimpleBroker(“/topic”); 方法的基础上又增加了一个 broker 前缀 “/queue” 方便对群发消息和点对点消息进行管理。
改造 Controller,如下
@Controller
public class GreetingController {
@Autowired
SimpMessagingTemplate messagingTemplate;
@MessageMapping("/hello")
public void greeting(Message message) throws Exception {
messagingTemplate.convertAndSend("/topic/greetings");
}
@MessageMapping("/chat")
public void chat(Principal principal, Chat chat) {
String from = principal.getName();
chat.setFrom(from);
messagingTemplate.convertAndSendToUser(chat.getTo(),"/queue/chat", chat);
}
}
代码解释:
public void convertAndSendToUser(String user, String destination, Object payload, @Nullable Map<String, Object> headers, @Nullable MessagePostProcessor postProcessor) throws MessagingException {
Assert.notNull(user, "User must not be null");
user = StringUtils.replace(user, "/", "%2F");
destination = destination.startsWith("/") ? destination : "/" + destination;
super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);
}
在 resources/static 目录下创建 onlinechat.html 页面作为在线聊天页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>单聊</title> <script src="/webjars/jquery/jquery.min.js"></script> <script src="/webjars/sockjs-client/sockjs.min.js"></script> <script src="/webjars/stomp-websocket/stomp.min.js"></script> <script src="/chat.js"></script> </head> <body> <div id="chat"> <div id="chatsContent"> </div> <div> 请输入聊天内容: <input type="text" id="content" placeholder="聊天内容"> 目标用户: <input type="text" id="to" placeholder="目标用户"> <button id="send" type="button">发送</button> </div> </div> </body> </html>
为了演示方便,这里需要手动输入目标用户名。相关 js 如下:
var stompClient = null; function connect() { var socket = new SockJS('/chat'); stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { stompClient.subscribe('/user/queue/chat', function (chat) { showGreeting(JSON.parse(chat.body)); }); }); } function sendMsg() { stompClient.send("/app/chat", {}, JSON.stringify({'content':$("#content").val(), 'to':$("#to").val()})); } function showGreeting(message) { $("#chatsContent") .append("<div>" + message.from+":"+message.content + "</div>"); } $(function () { connect(); $( "#send" ).click(function() { sendMsg(); }); });
chat.js 与 app.js 的差异:
http://localhost:8080/onlinechat.html,在谷歌登录 admin/123 用户,在 Microsoft Edge 中登录 sang/123 用户,然后admin发送消息给sang
接着 sang 发消息给amdin
sang 给自己发消息
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。