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

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

声明:

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

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

如有侵权,请联系我删除

文章目录

  • SpringBoot 学习笔记_整合 WebSocket —— 消息点对点发送
    • SpringBoot 整合 WebSocket
      • SpringBoot 整合 WebSocket —— 消息点对点发送(单聊)

SpringBoot 整合 WebSocket

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

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

  • 创建 SpringBoot 项目,添加依赖

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-websocketartifactId>
    dependency>
    
    
    <dependency>
        <groupId>org.webjarsgroupId>
        <artifactId>webjars-locator-coreartifactId>
    dependency>
    
    
    <dependency>
        <groupId>org.webjarsgroupId>
        <artifactId>sockjs-clientartifactId>
        <version>1.1.2version>
    dependency>
    
    <dependency>
        <groupId>org.webjarsgroupId>
        <artifactId>stomp-websocketartifactId>
        <version>2.3.3version>
    dependency>
    
    
    <dependency>
        <groupId>org.webjarsgroupId>
        <artifactId>jqueryartifactId>
        <version>3.3.1version>
    dependency>
    

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

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-securityartifactId>
    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);
        }
    }
    
  • 构建聊天页面

    
    <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(`
    ${ message.from}:${ message.content}
    `
    ) } $(function () { connect(); $('#send').click(() => sendMsg()); })

你可能感兴趣的:(#,●,SpringBoot,spring,boot,java,websocket)