Spring Boot(七):使用webSocket组件创建一对一的聊天室

在这之前,已经做过一个Spring Boot使用WebSocket的广播式运用:
Spring Boot(五):webSocket组件的入门简单使用
现在,我们再来进一步了解webSocket;
下面我们使用webSocket组件创建一对一的聊天室:

一.首先先创建前端页面,代码如下图所示:

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)创建STOMP协议的webSocket客户端。-->
    stomp.connect('guest', 'guest', function(frame) {//3<!--(3)连接webSocket的服务端。-->
        stomp.subscribe("/user/queue/notifications", handleNotification);////4 通过stompClient.subscribe()订阅服务器的目标是'/queue/notifications'发送过来的地址,
    });

    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中创建spring Security,代码如下图所示:

1.设置主页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(){
        //当然,如果你有自己的加密方法,这个方法就写自己的加密方法好了
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置需要登入验证
//        http.formLogin()          // 定义当需要用户登录时候,转到的登录页面。
//                .and()
//                .authorizeRequests()    // 定义哪些URL需要被保护、哪些不需要被保护
//                .anyRequest()        // 任何请求,登录后可以访问
//                .authenticated();
    //配置需要登入验证的自定义配置
        http.formLogin()          // 定义当需要用户登录时候,转到的登录页面。
                .loginPage("/login.html")      // 设置登录页面
                .loginProcessingUrl("/user/login") // 自定义的登录接口
                .and()
                .authorizeRequests()    // 定义哪些URL需要被保护、哪些不需要被保护
                .antMatchers("/login.html").permitAll()   // 设置所有人都可以访问登录页面
                .anyRequest()        // 任何请求,登录后可以访问
                .authenticated()
                .and()
                .csrf().disable();     // 关闭csrf防护

        //配置不需要登入验证
//        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忽略静态资源的拦截
    @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功能类似,用于设置URL映射地址,浏览器向服务器发起请求,需要通过该地址。
    @SendTo("/topic/getResponse")//2如果服务器接受到了消息,就会对订阅了@SendTo括号中的地址传送消息
    public WiselyResponse say(WiselyMessage message) throws Exception {
        return new WiselyResponse("你好, " + message.getName() + "!");
    }
    @Autowired
    private SimpMessagingTemplate messagingTemplate;//1 SimpMessagingTemplate用于向浏览器发送信息。

    @MessageMapping("/chat")
    public void handleChat(Principal principal, String msg) { //2在spring mvc中,principal包含了登录用户的信息,在这里我们直接用。
        if (principal.getName().equals("user1")) {//3这里是一段写死的代码,如果登录的用户是dzz,那就将消息发送给zbb,大家根据自己的需求进行修改。通过convertAndSendToUser()方法进行发送,第一个参数是信息接收的目标,第二个参数是要发送的地址,第三个参数是要发送的信息。
            messagingTemplate.convertAndSendToUser("user2",
                    "/queue/notifications", principal.getName() + "-send:"
                            + msg);
        } else {
            messagingTemplate.convertAndSendToUser("user2",
                    "/queue/notifications", principal.getName() + "-send:"
                            + msg);
        }
    }
}

4.在原来的WebSocketConfig类中再创建一个节点(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协议来传输基于代理(MessageBroker)的消息,这时候控制器(controller)开始支持@MessageMapping,就像是使用@requestMapping一样。
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/stomp").withSockJS(); //(2),注册一个Stomp的节点(endpoint),并指定使用SockJS协议。
        //stomp就是websocket的端点,客户端需要注册这个端点进行链接,withSockJS允许客户端利用sockjs进行浏览器兼容性处理
    }
//    @Override
//    public void configureMessageBroker(MessageBrokerRegistry registry) {
//        registry.enableSimpleBroker("/topic");//(3),配置消息代理(MessageBroker)。
//        //设置服务器广播消息的基础路径
////        registry.setApplicationDestinationPrefixes("/app");//设置客户端订阅消息的基础路径
//    }
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //设置一对一聊天室的基础路径
        registry.enableSimpleBroker("/queue","/topic"); 
    }
}

三.效果演示

启动项目,分别在两个浏览器进行访问,一个用账号user1一个用账号user2,密码都是123:
登入并发送消息,结果如下:


图片.png

另一个账号的界面就可以看到:


图片.png

图片.png

最后附上项目结构:
图片.png

项目还是放在原来的github目录上;

THAT IS OVER

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352