SpringBoot+WebSocket点对点消息通讯应用

1、在pom文件中添加SpringSecurity依赖

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

2、简单配置SpringSecurity的登录操作

package com.websocket.onetoone.config;
import com.websocket.onetoone.utils.MyPasswordEncoder;
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;

/**
 * @ClassName WebSecurityConfig
 * @Description TODO
 * @Author shanzz
 * @Date 2019/1/2 11:08
 * @Version 1.0
 **/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/","/login").permitAll()//设置SpringSecurity对/和”/login"路径不拦截
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")//设置SpringSecurity 的登录页面访问的路径为/login
                .defaultSuccessUrl("/chat")//登录成功后转向/chat路径
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }

    //在内存中分别配置两个用户szz和wisely,密码和用户名一致,角色名是USER
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder())
                .withUser("szz").password("szz").roles("USER")
                .and()
                .withUser("wisely").password("wisely").roles("USER");
    }

    // /resource/static/目录下的静态资源,SpringSecurity不拦截
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/static/**");
    }
}

Spring boot 2.x引用的security 依赖是 spring security 5.X版本,此版本需要提供一个PasswordEncorder的实例,否则后台汇报错误:
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

创建 MyPasswordEncoder实例

package com.websocket.onetoone.utils;

import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @ClassName MyPasswordEncoder
 * @Description TODO
 * @Author shanzz
 * @Date 2019/1/2 13:50
 * @Version 1.0
 **/
public class MyPasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence charSequence) {
        return charSequence.toString();
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return s.equals(charSequence.toString());
    }
}

3、配置websocket

package com.websocket.onetoone.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

/**
 * @ClassName WebSocketConfig
 * @Description TODO
 * @Author shanzz
 * @Date 2019/1/2 13:08
 * @Version 1.0
 **/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/endpointWisely").withSockJS();
        registry.addEndpoint("/endpointChat").withSockJS();//注册一个名为endpointChat的endpoint
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/queue","/topic");//点对点式增加一个/queue消息代理
    }
}

4、配置控制器 

package com.websocket.onetoone.controller;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;

import java.security.Principal;

/**
 * @ClassName WebSocketController
 * @Description TODO
 * @Author shanzz
 * @Date 2019/1/2 13:14
 * @Version 1.0
 **/

@Controller
public class WebSocketController {
    @Autowired
    private SimpMessagingTemplate messagingTemplate;//通过SimpMessagingTemplate向浏览器发送消息

    @MessageMapping("/chat")
    public void handleChat(Principal principal,String msg){//在SpringMVC中,可以直接在参数中获得principal,pinciple中包含当前用户的信息

      /**
       * 这里是一段死代码,如果发送人是szz,则发送给wisely;如果发送人是wisely,则发送给szz.
       * 可以根据自己的业务逻辑改造此段代码
      **/

        if (principal.getName().equals("szz")){
            /**
             *通过messagingTemplate.convertAndSendToUser向用户发送消息,
             * 第一个参数是接收消息的用户
             * 第二个是浏览器订阅的地址
             * 第三个是消息本身
            **/

            messagingTemplate.convertAndSendToUser("wisely",
                    "/queue/notifications",principal.getName()+"-send:"+msg);
        }else{
            messagingTemplate.convertAndSendToUser("szz","/queue/notifications",principal.getName()+"-send:"+msg);
        }

    }

}

5、登录界面

<!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">
<meta charset="UTF-8" />
<head>
    <title>Home</title>
    <script th:src="@{sockjs.min.js}"></script>
    <script th:src="@{stomp.min.js}"></script>
    <script th:src="@{jquery.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("/endpointChat"); //连接endpoint名称为"/endpointChat"的endpoint
    var stomp = Stomp.over(sock);
    stomp.connect('guest', 'guest', function(frame) {
        /*
        *订阅/user/queue/notifications发送的消息,这里与在控制器的messagingTemplate.convertAndSendToUser中定义的订阅地址保持一致。
        * 这里多了一个/user,并且这个/user是必须的,使用了/user才会发送到指定的用户
        */
        stomp.subscribe("/user/queue/notifications", handleNotification);//
    });



    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>

6、添加页面解析器

package com.websocket.onetoone.config;

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

/**
 * @ClassName WebMvcConfig
 * @Description TODO
 * @Author shanzz
 * @Date 2019/1/2 13:32
 * @Version 1.0
 **/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("/login");
        registry.addViewController("/chat").setViewName("/chat");
    }
}

 7、运行程序,访问

参考书籍:JavaEE开发的颠覆者:SpringBoot实战第七章 7.6.3 

猜你喜欢

转载自blog.csdn.net/szzzgyn/article/details/85612970