SpringBoot 学习笔记_整合 WebSocket —— 消息点对点发送

SpringBoot 学习笔记_整合 WebSocket —— 消息点对点发送

声明:

本次学习参考 《SpringBoot + Vue 开发实战》 · 王松(著) 一书。

本文的目的是记录我学习的过程和遇到的一些问题以及解决办法,其内容主要来源于原书。

如有侵权,请联系我删除

SpringBoot 整合 WebSocket

SpringBoot 整合 WebSocket —— 消息点对点发送(单聊)

SpringBoot 对 WebSocket 提供了非常友好的支持,可以方便开发者在项目中快速集成 WebSocket 功能,实现单聊或者群聊。

  • 创建 SpringBoot 项目,添加依赖

    <!--    添加 WebSocket 依赖    -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    
    <!--    添加 webjars 依赖    -->
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>webjars-locator-core</artifactId>
    </dependency>
    
    <!--    添加 sockjs-client 依赖    -->
    <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>
    
    <!--    添加 jquery 依赖    -->
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>jquery</artifactId>
        <version>3.3.1</version>
    </dependency>
    

    既然要点位点单聊发送消息,就应该有用户的概念,因此还需要 Spring Security 依赖

    <!--    添加 Spring Security 依赖    -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
  • 配置 Spring Security

    /**
     * Spring Security 配置
     */
    @Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        @Bean
        PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        /**
         * 创建两个用户。
         *
         * 用户名 : admin  角色 :admin   密码 : 123
         * 用户名 : sang   角色: user    密码: 123
         *
         * @param auth
         * @throws Exception
         */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                    .withUser("admin")
                    .password("$2a$10$8nd6PQJkSYRcZJivePm3y.0/Bk3OO0mSOal5P2/oSPTGjjt8/KLra")
                    .roles("admin")
                    .and()
                    .withUser("sang")
                    .password("$2a$10$8nd6PQJkSYRcZJivePm3y.0/Bk3OO0mSOal5P2/oSPTGjjt8/KLra")
                    .roles("user");
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .anyRequest()
                    .authenticated()
                    .and()
                    .formLogin()
                    .permitAll();
        }
    }
    
  • 配置 WebSocket

    /**
     * 自定义类 WebSocketConfig 继承自 WebSocketMessageBrokerConfigurer 进行 WebSocket 配置
     */
    @Configuration
    // @EnableWebSocketMessageBroker 注解开启 WebSocket 消息代理
    @EnableWebSocketMessageBroker
    public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
        @Override
        public void configureMessageBroker(MessageBrokerRegistry registry) {
            // 设置消息代理前缀
            // 即如果消息前缀是 /topic 就会将消息转发给消息代理(broker),再由消息代理将消息广播给当前连接的客户端
    
            // 在群发的基础上增加一个 broker 前缀 "/queue" 方便对群发消息和点对点消息进行管理
            registry.enableSimpleBroker("/topic", "/queue");
            // 配置一个或多个前缀,通过这些前缀过滤出需要被注解方法处理的消息
            // 例如,前缀 /app 的 destination 可以通过 @MessageMapping 注解的方法处理
            // 而其他 destination(/topic  /queue) 将被直接交给 broker 处理
            registry.setApplicationDestinationPrefixes("/app");
        }
    
        @Override
        public void registerStompEndpoints(StompEndpointRegistry registry) {
            // 定义一个前缀为 /chat 的 endPoint, 并开启 sockjs 支持,sockjs 可以解决浏览器对 WebSocket 的兼容性问题
            // 客户端将通过这里配置的 URL 来建立 WebSocket 连接
            registry.addEndpoint("/chat").withSockJS();
        }
    }
    
  • 定义实体类

    public class Chat {
        private String to;
        private String from;
        private String content;
        /* Getter & Setter */
    }
    
  • 定义 Controller

    @Controller
    public class GreetingController {
    
        /**
         * 群发消息,与之前相同,依然用 @SendTo 实现
         */
        // 用来接收 /app/hello 路径发送来的消息,在注解方法中对消息进行处理后,再将消息转发到 @SendTo 定义的路径上
        @MessageMapping("/hello")
        // @SendTo 路径是一个前缀为 /topic 的路径,因此该消息将被交给消息代理 broker,再由 broker 进行广播
        @SendTo("/topic/greetings")
        public Message greeting(Message message) throws Exception{
            return message;
        }
    
    
        /**
         * 点对点发送消息则使用 SimpMessagingTemplate 来实现
         */
        @Autowired
        SimpMessagingTemplate messagingTemplate;
        // 表示处理来自 /app/chat 的消息
        @MessageMapping("/chat")
        // 第一个参数 Principal 用来获取当前登录用户的信息,第二个参数为客户端发送来的消息
        public void chat(Principal principal, Chat chat){
            // 获取当前用户的用户名
            String from =  principal.getName();
            // 设置给 chat 对象的 from 属性
            chat.setFrom(from);
            // 将消息发送出去,发送目标就是 chat 对象的 to 属性。
            // 该方法内部调用了 convertAndSend 方法,并对消息进行了处理
            messagingTemplate.convertAndSendToUser(chat.getTo(), "/queue/chat", chat);
        }
    }
    
  • 构建聊天页面

    <!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">发送</button>
        </div>
    </div>
    </body>
    </html>
    
    let stompClient = null;
    
    function connect() {
        let socket = new SockJS('/chat');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, frame =>
            // 连接成功后,订阅的地址为 /user/queue/chat
            // 该地址比服务端配置多了 /user 前缀,这是因为 SimpMessagingTemplate 类中自动添加了路径前缀
            stompClient.subscribe('/user/queue/chat', chat => showGreeting(JSON.parse(chat.body)))
        )
    }
    
    function sendMsg() {
        // 聊天消息发送路径 /app/chat
        stompClient.send('/app/chat', {}, JSON.stringify({'content': $('#content').val(), 'to': $('#to').val()}));
        showGreeting({from: '我', content: $('#content').val()})
    }
    
    function showGreeting(message) {
        $('#chatsContent').append(`<div>${message.from}:${message.content}</div>`)
    }
    
    $(function () {
        connect();
        $('#send').click(() => sendMsg());
    })
    
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HolaSecurity

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值