Spring Boot(七):使用webSocket組件創(chuàng)建一對一的聊天室

在這之前,已經(jīng)做過一個Spring Boot使用WebSocket的廣播式運用:
Spring Boot(五):webSocket組件的入門簡單使用
現(xiàn)在榔昔,我們再來進(jìn)一步了解webSocket;
下面我們使用webSocket組件創(chuàng)建一對一的聊天室:

一.首先先創(chuàng)建前端頁面嘹朗,代碼如下圖所示:

1.login.html


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登錄頁面</title>
</head>
<body>
<h2>自定義登錄頁面</h2>
<form action="/user/login" method="post">
    <table>
        <tr>
            <td>用戶名:</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密碼:</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td colspan="2"><button type="submit">登錄</button></td>
        </tr>
    </table>
</form>
</body>
</html>

2.chat.html


<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8" />
<head>
    <title>Home</title>
    <script src="../js/jquery-2.1.1.min.js"></script>
    <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>
</head>
<body>
<p>
    賴賴聊天室
</p>

<form id="wiselyForm">
    <textarea rows="4" cols="60" name="text"></textarea>
    <input type="submit"/>
</form>

<script th:inline="javascript">
    $('#wiselyForm').submit(function(e){
        e.preventDefault();
        var text = $('#wiselyForm').find('textarea[name="text"]').val();
        sendSpittle(text);
    });

    var sock = new SockJS("/stomp"); //1連接SockJS的endpoint是“stomp”屹培,與后臺代碼中注冊的endpoint要一樣惫谤。-
    var stomp = Stomp.over(sock);//2<!--(2)創(chuàng)建STOMP協(xié)議的webSocket客戶端溜歪。-->
    stomp.connect('guest', 'guest', function(frame) {//3<!--(3)連接webSocket的服務(wù)端。-->
        stomp.subscribe("/user/queue/notifications", handleNotification);////4 通過stompClient.subscribe()訂閱服務(wù)器的目標(biāo)是'/queue/notifications'發(fā)送過來的地址蝴猪,
    });

    function handleNotification(message) {
        $('#output').append("<b>Received: " + message.body + "</b><br/>")
    }

    function sendSpittle(text) {
        stomp.send("/chat", {}, text);//3
    }
    $('#stop').click(function() {sock.close()});
</script>

<div id="output"></div>
</body>
</html>

二.在spring boot中創(chuàng)建spring Security自阱,代碼如下圖所示:

1.設(shè)置主頁IndexPageConfig沛豌;

package com.example.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.RedirectView;

/**
 * @author AxeLai
 * @date 2019-04-29 09:31
 */
@Controller
public class IndexPageConfig {
    @RequestMapping("/")
    public RedirectView ws() {
        return new RedirectView("/pages/chat.html");
    }
}

2.SecurityConfig類:

package com.example.demo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author AxeLai
 * @date 2019-04-30 15:15
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Bean
    public PasswordEncoder passwordEncoder(){
        //當(dāng)然加派,如果你有自己的加密方法,這個方法就寫自己的加密方法好了
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置需要登入驗證
//        http.formLogin()          // 定義當(dāng)需要用戶登錄時候竹勉,轉(zhuǎn)到的登錄頁面次乓。
//                .and()
//                .authorizeRequests()    // 定義哪些URL需要被保護(hù)孽水、哪些不需要被保護(hù)
//                .anyRequest()        // 任何請求,登錄后可以訪問
//                .authenticated();
    //配置需要登入驗證的自定義配置
        http.formLogin()          // 定義當(dāng)需要用戶登錄時候,轉(zhuǎn)到的登錄頁面丧慈。
                .loginPage("/login.html")      // 設(shè)置登錄頁面
                .loginProcessingUrl("/user/login") // 自定義的登錄接口
                .and()
                .authorizeRequests()    // 定義哪些URL需要被保護(hù)主卫、哪些不需要被保護(hù)
                .antMatchers("/login.html").permitAll()   // 設(shè)置所有人都可以訪問登錄頁面
                .anyRequest()        // 任何請求,登錄后可以訪問
                .authenticated()
                .and()
                .csrf().disable();     // 關(guān)閉csrf防護(hù)

        //配置不需要登入驗證
//        http.authorizeRequests()
//                .anyRequest()
//                .permitAll()
//                .and()
//                .logout()
//                .permitAll();
    }
    //4
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("user1").password(passwordEncoder.encode("123")).roles("USER")//這里兩個是分別賬號和密碼(賬號:user1密碼:123)
                .and()
                .withUser("user2").password(passwordEncoder.encode("123")).roles("USER");//這里兩個是分別賬號和密碼(賬號:user2密碼:123)
    }
    //5忽略靜態(tài)資源的攔截
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/static/**");
    }
}

3.在原來的WsController類中增加聊天的方法handleChat()簇搅,代碼如下圖所示:

package com.example.demo.controller;

import com.example.demo.model.WiselyMessage;
import com.example.demo.model.WiselyResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;

import java.security.Principal;

/**
 * @author AxeLai
 * @date 2019-04-29 09:08
 */
@Controller
public class WsController {
    @MessageMapping("/welcome")//1@MessageMapping和@RequestMapping功能類似瘩将,用于設(shè)置URL映射地址,瀏覽器向服務(wù)器發(fā)起請求姿现,需要通過該地址。
    @SendTo("/topic/getResponse")//2如果服務(wù)器接受到了消息异旧,就會對訂閱了@SendTo括號中的地址傳送消息
    public WiselyResponse say(WiselyMessage message) throws Exception {
        return new WiselyResponse("你好, " + message.getName() + "!");
    }
    @Autowired
    private SimpMessagingTemplate messagingTemplate;//1 SimpMessagingTemplate用于向瀏覽器發(fā)送信息吮蛹。

    @MessageMapping("/chat")
    public void handleChat(Principal principal, String msg) { //2在spring mvc中,principal包含了登錄用戶的信息潮针,在這里我們直接用每篷。
        if (principal.getName().equals("user1")) {//3這里是一段寫死的代碼,如果登錄的用戶是dzz,那就將消息發(fā)送給zbb带兜,大家根據(jù)自己的需求進(jìn)行修改吨灭。通過convertAndSendToUser()方法進(jìn)行發(fā)送刑巧,第一個參數(shù)是信息接收的目標(biāo),第二個參數(shù)是要發(fā)送的地址吠冤,第三個參數(shù)是要發(fā)送的信息恭理。
            messagingTemplate.convertAndSendToUser("user2",
                    "/queue/notifications", principal.getName() + "-send:"
                            + msg);
        } else {
            messagingTemplate.convertAndSendToUser("user2",
                    "/queue/notifications", principal.getName() + "-send:"
                            + msg);
        }
    }
}

4.在原來的WebSocketConfig類中再創(chuàng)建一個節(jié)點(endpoint)和代理(MessageBroker),代碼如下圖所示:

package com.example.demo.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.StompWebSocketEndpointRegistration;

/**
 * Created by AxeLai on 2019/04/29 0011.
 */
@Configuration
@EnableWebSocketMessageBroker//(1),@EnableWebSocketMessageBroker注解用于開啟使用STOMP協(xié)議來傳輸基于代理(MessageBroker)的消息涯保,這時候控制器(controller)開始支持@MessageMapping,就像是使用@requestMapping一樣夕春。
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/stomp").withSockJS(); //(2),注冊一個Stomp的節(jié)點(endpoint),并指定使用SockJS協(xié)議专挪。
        //stomp就是websocket的端點,客戶端需要注冊這個端點進(jìn)行鏈接速侈,withSockJS允許客戶端利用sockjs進(jìn)行瀏覽器兼容性處理
    }
//    @Override
//    public void configureMessageBroker(MessageBrokerRegistry registry) {
//        registry.enableSimpleBroker("/topic");//(3),配置消息代理(MessageBroker)倚搬。
//        //設(shè)置服務(wù)器廣播消息的基礎(chǔ)路徑
////        registry.setApplicationDestinationPrefixes("/app");//設(shè)置客戶端訂閱消息的基礎(chǔ)路徑
//    }
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //設(shè)置一對一聊天室的基礎(chǔ)路徑
        registry.enableSimpleBroker("/queue","/topic"); 
    }
}

三.效果演示

啟動項目潭枣,分別在兩個瀏覽器進(jìn)行訪問,一個用賬號user1一個用賬號user2盆犁,密碼都是123:
登入并發(fā)送消息谐岁,結(jié)果如下:


圖片.png

另一個賬號的界面就可以看到:


圖片.png

圖片.png

最后附上項目結(jié)構(gòu):
圖片.png

項目還是放在原來的github目錄上窜司;

THAT IS OVER

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末航揉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子惫企,更是在濱河造成了極大的恐慌媳友,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哼御,死亡現(xiàn)場離奇詭異焊唬,居然都是意外死亡,警方通過查閱死者的電腦和手機焰雕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門矩屁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人爵赵,你說我怎么就攤上這事吝秕。” “怎么了空幻?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵烁峭,是天一觀的道長。 經(jīng)常有香客問我秕铛,道長约郁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任但两,我火速辦了婚禮鬓梅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谨湘。我一直安慰自己绽快,他們只是感情好芥丧,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著物遇,像睡著了一般挎挖。 火紅的嫁衣襯著肌膚如雪崔涂。 梳的紋絲不亂的頭發(fā)上冷蚂,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機與錄音隆夯,去河邊找鬼忧额。 笑死睦番,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的示启。 我是一名探鬼主播丑搔,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼浙垫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起辙售,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎士八,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝗茁,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了佩研。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旬薯。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖抚官,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情倍奢,我是刑警寧澤卒煞,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布碉碉,位于F島的核電站,受9級特大地震影響蜡吧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜君仆,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一牍鞠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧萤晴,春花似錦店读、人聲如沸净宵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽已添。三九已至坎吻,卻和暖如春刊头,著一層夾襖步出監(jiān)牢的瞬間您机,已是汗流浹背被碗。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工衣迷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓痛阻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親录淡。 傳聞我的和親對象是個殘疾皇子免钻,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355