SpringBoot2实战训练demo3之WebSocket点对点发送消息

在WebSocket广播式的基础上修改即可

第一步:我们在pom.xml配置文件中增加Spring Security

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>


第二步:创建Spring Security的简单配置类
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * Spring Security的简单配置
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception{
        http
                .authorizeRequests()
                //设置Spring Security对/和/“login”路径不拦截
                .antMatchers("/","/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                //设置Spring Security的登录页面访问的路径为/login
                .loginPage("/login")
                //登陆成功后转向/chat路径
                .defaultSuccessUrl("/chat")
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }
    //在内存中分别配置两个用户aaa和bbb,密码和用户名一致,角色是USER
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .passwordEncoder(passwordEncoder())
                .withUser("aaa").password(passwordEncoder().encode("aaa")).roles("USER")
                .and()
                .withUser("bbb").password(passwordEncoder().encode("bbb")).roles("USER");
    }
    //Spring Security不拦截"/resources/static/"目录下的静态资源
    @Override
    public void configure(WebSecurity web) throws Exception{
        web.ignoring().antMatchers("/resources/static/**");
    }
    //密码处理
    private static PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

}

 第三步:配置WebSocket,注册一个名为/endpointChatendpoint,点对点式应增加一个/queue消息代理

 

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;

@Configuration
//通过@EnableWebSocketMessageBroker注解开启使用STOMP协议来传输基于代理(message broker)的消息,
// 这时控制器支持使用@MessageMapping,就像使用@RequestMapping一样
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    //注册STOMP协议的节点(endpoint),并映射的指定的URL
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry){
        //注册一个STOMP的endpoint,并指定使用SocketJS协议
        registry.addEndpoint("/endpointDemo").withSockJS();
        //注册一个名为/endpointChat的endpoint
        registry.addEndpoint("/endpointChat").withSockJS();
    }
    //配置消息代理(Message Broker)
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry){
        //广播式应配置一个/topic消息代理
        //点对点式应增加一个/queue消息代理
        registry.enableSimpleBroker("/queue","/topic");
    }
}

 

 第四步:控制器WsController更改

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;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * 演示控制器
 */
@Controller
public class WsController {

    //通过SimpMessagingTemplate向浏览器发送消息
    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    //当浏览器向服务端发送请求时,通过@MessageMapping映射/welcome这个地址,类似于@RequestMapping
    @MessageMapping("/welcome")
    //当服务端有消息时,会对订阅了@SendTo中的路径的浏览器发送消息
    @SendTo("/topic/getResponse")
    public DemoResponse say(DemoMessage message) throws Exception {
        Thread.sleep(3000);
        return new DemoResponse("欢迎你:" + message.getName() + "!");
    }

    /**
     * 点对点
     */


    //在Spring MVC中,可以直接在参数中获得principal,pinciple中包含当前用户的信息
    @MessageMapping("/chat")
    public void handleChat(Principal principal, String msg) {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //这里是一段硬编码,如果发送人是aaa,则发送给bbb;如果发送人是bbb,则发送给aaa,
        if (principal.getName().equals("aaa")) {
            messagingTemplate.convertAndSendToUser("bbb",
                    "/queue/notifications", principal.getName() + "-发来消息:" + msg + "\t"  +sdf.format(date));
        } //通过messagingTemplate.convertAndSendToUser向用户发送消息,第一个参数是接收消息的用户,
        // 第二个是浏览器订阅的地址,第三个是消息本身
        else {
            messagingTemplate.convertAndSendToUser("aaa",
                    "/queue/notifications", principal.getName() + "-发来消息:" + msg + "   "  +sdf.format(date));
        }
    }

}

 第五步:创建登录页面

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
        xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<meta charset="UTF-8">
<head>
    <title>登录页面</title>
</head>
<body>
    <div th:if="${param.error}">
        无效的账号和密码
    </div>
<div th:if="${param.logout}">
    你已注销
</div>
<form th:action="@{/login}" method="post">
    <div>
        <label>账号:<input type="text" name="username"/></label>
    </div>
    <div>
        <label>密码:<input type="password" name="password"/></label>
    </div>
    <div>
        <input type="submit" value="登录"/>
    </div>
</form>
</body>
</html>

 第六步:创建简单聊天页面

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>聊天室</title>
    <script th:src="@{sockjs.min.js}"></script>
    <script th:src="@{stomp.min.js}"></script>
    <script th:src="@{jquery.min.js}"></script>
</head>
<body>
<p>
    聊天室
</p>
<form id="demoFrom">
    <textarea rows="4" cols="60" name="text"></textarea><br>
    <input type="submit" value="发送消息"/>
</form>
<script th:inline="javascript">
    $('#demoFrom').submit(function (e) {
        e.preventDefault();
        var text = $('#demoFrom').find('textarea[name="text"]').val();
        sendSpittle(text);
    });
    //连接endpoint名称为“/endpointChat”的endpoint
    var sock = new SockJS("/endpointChat");
    var stomp = Stomp.over(sock);
    stomp.connect('guest', 'guest', function (frame) {
        //订阅/user/queue/notifications 发送的消息,这里与在控制器的messagingTemplate.converAndSendToUser中
        //定义的订阅地址保持一致。这里的/user是必须的,使用了/user才会发送消息到指定的用户
        stomp.subscribe("/user/queue/notifications", handleNotification);
    });

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

    function sendSpittle(text) {
        //
        stomp.send("/chat", {}, text);
    }

    $('#stop').click(function () {
        sock.close()
    });

</script>

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

 第七步:增加页面的viewController

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * 配置viewController,为ws.html提供便捷的路径映射
 */
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry){
        registry.addViewController("/ws").setViewName("/ws");
        registry.addViewController("/login").setViewName("/login");
        registry.addViewController("/chat").setViewName("/chat");
    }
}

 第八步:运行看效果:

两个浏览器登录不同账户

 

 登录后给对方发送消息

 

 

 

 

posted @ 2019-11-21 10:23  乡野小猫  阅读(338)  评论(0)    收藏  举报