1、前言javascript
玩.net的時候,在asp.net下有一個叫 SignalR 的框架,能夠在ASP .NET的Web項目中實現實時通訊。剛接觸java尋找相關替代品,發現 java 體系中有一套基於stomp協議的websocket通訊的框架,websocket是什麼能夠參考阮老大的《WebSocket 教程》,這篇文章不討論理論知識,這裏只講應用,把websocket的廣播模式與一對一模式一塊兒整理一個demo給你們分享一下。css
2、項目結構html
由於一對一私聊模式 使用principal的name做爲目的地標識。發給消息來源的那個用戶,該操做是認爲用戶登陸而且受權認證,因此這裏使用Spring Security來控制身份認證,項目結構以下:java
1.WebSecurityConfig: Spring Security安全控制類jquery
2.WebSocketConfig: web socket 控制類web
3. DefaultController:mvc控制器spring
4.ChatMessage: 消息實體對象數據庫
5.chat.html : 聊天消息發送接收html客戶端頁面跨域
6.login.html:登陸頁瀏覽器
pom.xml依賴以下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
3、代碼實現
1.web服務器安全配置
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { //注入密碼加密解密方式,由於這裏使用明文不加密 @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() //在內存中分別配置兩個用戶user1 user2和密碼 ,角色是user,持久化到數據庫中的本身配置不在本文知識範圍內 .withUser("user1").password("123").roles("user") .and() .withUser("user2").password("123").roles("user"); } @Override public void configure(WebSecurity web) throws Exception { ///resources/static/ 目錄下的靜態資源,spring security不攔截 web.ignoring().antMatchers("/resources/static/**","/resources/templates/**"); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() //設置spring security對 / 和 /login 路徑不攔截 .antMatchers("/", "/login").permitAll() .anyRequest().authenticated() .and() .formLogin() //設置spring security的登陸頁面訪問路徑爲 /login .loginPage("/login") //登錄成功後轉向 /chat 路徑 .defaultSuccessUrl("/chat.html") .permitAll() .and() .logout() .permitAll(); } }
2.WebSocket 配置類
/** * WebSocket 配置類 * Created by ejiyuan on 2018-7-11. */ @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { //容許客戶端使用socketJs方式訪問,訪問點爲ws,容許跨域 registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { //訂閱廣播 Broker(消息代理)名稱 registry.enableSimpleBroker("/topic"); // Enables a simple in-memory broker //全局使用的訂閱前綴(客戶端訂閱路徑上會體現出來) registry.setApplicationDestinationPrefixes("/app/"); //點對點使用的訂閱前綴(客戶端訂閱路徑上會體現出來),不設置的話,默認也是/user/ registry.setUserDestinationPrefix("/user/"); } }
3.控制器
@Controller public class DefaultController { @GetMapping("/") @ResponseBody public String helloWord() { return "helloWord"; } @GetMapping("/login") public String login() { return "login"; } //注入SimpMessagingTemplate 用於點對點消息發送 @Autowired private SimpMessagingTemplate messagingTemplate; @MessageMapping("/sendPublicMessage") //這裏是客戶端發送消息對應的路徑,等於configureMessageBroker中配置的setApplicationDestinationPrefixes + 這路徑即 /app/sendPublicMessage @SendTo("/topic/public") //也可使用 messagingTemplate.convertAndSend(); 推送 public ChatMessage sendPublicMessage(@Payload ChatMessage chatMessage) { return chatMessage; } @MessageMapping("/sendPrivateMessage") //這裏是客戶端發送消息對應的路徑,等於configureMessageBroker中配置的setApplicationDestinationPrefixes + 這路徑即 /app/sendPrivateMessage public void sendPrivateMessage(@Payload ChatMessage msg,Principal principal) { msg.setSender(principal.getName()); //將消息推送到指定路徑上 messagingTemplate.convertAndSendToUser(msg.getReceiver(), "topic/chat", msg); } /* 註釋方式推不過去這裏沒調通,有大神的話慢慢研究吧 @SendToUser(value = "/topic/chat",broadcast=false) public ChatMessage sendPrivateMessage(@Payload ChatMessage msg,Principal principal) { msg.setSender(principal.getName()); return msg; }*/ }
4.消息載體:pojo對象
/** * 消息載體 * Created by ejiyuan on 2018-7-11 */ @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY) public class ChatMessage { private String content; private String sender; private String receiver; public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getSender() { return sender; } public void setSender(String sender) { this.sender = sender; } public String getReceiver() { return receiver; } public void setReceiver(String receiver) { this.receiver = receiver; } }
5.客戶端聊天html頁面
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <meta charset="UTF-8"/> <head> <title>聊天框</title> <script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script> <script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script> <script src=" https://code.jquery.com/jquery-3.3.1.min.js"></script> <script type="text/javascript"> //ws /ws 的endpoint var sock = new SockJS('/ws'); //跟你的WebSocketConfig中配置要一致 var stomp = Stomp.over(sock); //創建鏈接監聽 stomp.connect({}, function (frame) { stomp.subscribe('/topic/public', function (response) { $("#output").append('<b>公共消息:' + response.body + '</b><br/>'); }); //訂閱 /user/topic/chat 發送的消息,這裏與 //在控制器的messagingTemplate.convertAndSendToUser中定義的訂閱地址保持一致 //這裏多了 /user ,而且這個 /user是必須的,使用了 /user 纔會將消息發送到指定用戶 stomp.subscribe("/user/topic/chat", function handleNotification(message) { console.log("msg" + message); $("#output").append('<b>' + message.body + '</b><br/>'); }); }); //發送私有消息指定的人能收到 function sendPrivateMsg() { stomp.send("/app/sendPrivateMessage", {}, JSON.stringify({ 'content': $("#text").val(), //消息內容 'receiver': $("#receiver").val() //接收人 })); } //發送公共消息 誰都能收到,本身也能收到 function sendPublicMsg() { stomp.send("/app/sendPublicMessage", {}, JSON.stringify({ 'content': $("#text").val(), //消息內容 })); } //斷開鏈接 function stop() { sock.close(); } </script> </head> <body> <div> <textarea rows="4" cols="60" name="text" id="text"> </textarea> <br/> 接收人: <input id="receiver" value=""/> <br/> <input type="button" value="私有消息" onclick="sendPrivateMsg()"/> <input type="button" value="公共消息" onclick="sendPublicMsg()"/> <input id="stop" type="button" onclick="stop()" value="斷開"/> </div> <div id="output"></div> </body> </html>
3、測試:
1,分別在兩個瀏覽器中打開,登陸user1與user2
2,發消息測試
3.斷開測試:斷開後不管公共消息私有消息都沒法再接收
5、源代碼:https://download.csdn.net/download/ejiyuan/10536333
6、參考文檔
1.WebSocket 教程:http://www.ruanyifeng.com/blog/2017/05/websocket.html
2.玩轉spring boot——websocket:https://www.cnblogs.com/GoodHelper/p/7078381.html
3.Spring Boot 開發私有即時通訊系統(WebSocket):https://www.jianshu.com/p/0f498adb3820