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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。