介绍

介绍如何使用websocket的插件

得益于spring secueity和spring websocket的优化集成,插件通过一些简单的代码就可以将websocket功能并入项目中。

版本历史

  • 0.2.1

使用

使用gradle 插件

添加插件

gradle中

    implementation('io.github.baseinsight:plugin-springsecurity-websocket-starter:x.x.x')

配置

在application.yml文件中加入如下的配置,注意base顶级项的合并

base:
    websocket:
      userDestinationPrefix: /user/      #用户的队列前缀
      stompEndpoint: /springWebsocket    #stomp的服务点名称
      allowedOrigins: '*'                #运行js跨域访问ws
      topicBroker: /topic                #topic的broker名称
      queueBroker: /queue                #队列的broker名称

具体配置描述

name 描述 使用情况

springWebsocket

stomp的服务点名称

页面中通过项目的contextPath+springWebsocket构建socket连接 如 new SockJS(ctx+'/springWebsocket')

userDestinationPrefix

用户的队列前缀

访问spring security用户自己的队列时增加的前缀 如 stompClient.subscribe('/user/queue/message' function (greeting) {。。。}

topicBroker

topic的broker名称

如 stompClient.subscribe('/topic/info' function (serverInfo) {。。。}

queueBroker

队列的broker名称

如 stompClient.subscribe('/user/queue/message' function (greeting) {。。。}

样例

创建controller,WebsocketController类 ,内容如下:

@Controller
class WebsocketController {
    @Autowired
    SpringSecurityService SpringSecurityService
    @Autowired
    SimpMessagingTemplate brokerMessagingTemplate

    @RequestMapping(value = "/websocket/index")
    public String index(Model model){
        BaseUser currentUser=SpringSecurityService.currentUser;
        model.addAttribute("baseUsers", BaseUser.findAllByEnabledAndUsernameNotEqual(true,currentUser.username,['order':'asc','sort':'username']));
        return "/websocket/index"
    }

    //公布信息(topic)
    @MessageMapping("/info")
    public void info(String message, Principal principal)  {
        brokerMessagingTemplate.convertAndSend("/topic/info","${principal.name}:${message}".toString());
    }
    //发送消息(queue)
    @MessageMapping("/message")
    public void message(String message, Principal principal)  {
        JSONObject jsonObject=new JSONObject(message);
        String receiver =jsonObject.getString("receiver");
        message =jsonObject.getString("message");
        brokerMessagingTemplate.convertAndSendToUser(receiver,"/queue/message","${principal.name}:${message}".toString());
    }
}

创建页面,创建Websocket目录,增加index.jsp (老版本的页面,请自行修改为themleaf3的html页面),内容如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<!DOCTYPE html>
<html >
<head>
    <title>websocket demo</title>
    <meta name="layout" content="main">
    <link href="${pageContext.request.contextPath}/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet">
    <script src="${pageContext.request.contextPath}/webjars/jquery/jquery.min.js"></script>
    <script src="${pageContext.request.contextPath}/webjars/sockjs-client/sockjs.min.js"></script>
    <script src="${pageContext.request.contextPath}/webjars/stomp-websocket/stomp.min.js"></script>
    <script>
        var stompClient = null;
        function setConnected(connected) {
            $("#connect").prop("disabled", connected);
            $("#disconnect").prop("disabled", !connected);
            if (connected) {
                $("#conversation").show();
            }else {
                $("#conversation").hide();
            }
            $("#topicTable").html("");
            $("#queueTable").html("");
        }
        function connect() {
            var socket = new SockJS(ctx+'/springWebsocket');
            stompClient = Stomp.over(socket);
            stompClient.connect({}, function (frame) {
                setConnected(true);
                //订阅主题
                stompClient.subscribe('/topic/info', function (serverInfo) {
                    showTopic(serverInfo.body);
                });
                //订阅自己的用户消息
                stompClient.subscribe('/user/queue/message', function (serverInfo) {
                    showQueue(serverInfo.body);
                });
            });
        }
        function disconnect() {
            if (stompClient !== null) {
                stompClient.disconnect();
            }
            setConnected(false);
        }

        function sendInfo() {
            //第一个参数发送的地址,第二个参数是头信息,第三个参数是消息体
            stompClient.send("/project/info", {},  $("#info").val());
        }
        function sendMessage() {
            stompClient.send("/project/message", {}, JSON.stringify({receiver:$('#receiver').val(),message:$("#message").val()}));
        }

        function showTopic(message) {
            $("#topicTable").append($("<tr><td>" + message + "</td></tr>"));
        }
        function showQueue(message) {
            $("#queueTable").append($("<tr><td>" + message + "</td></tr>"));
        }

    </script>
</head>
<body>
<div id="main-content" class="container">
    <div class="row">
        <div class="col-md-3">
                <div class="form-group">
                    <label for="connect">WebSocket connection:</label>
                    <button id="connect" class="btn btn-default" onclick="connect()" type="button">Connect</button>
                    <button id="disconnect" class="btn btn-default" onclick="disconnect()" type="button" disabled="disabled">Disconnect
                    </button>
                </div>
        </div>
        <div class="col-md-3">
            <div class="form-group">
                <label for="info"></label>
                <input type="text" id="info" class="form-control" placeholder="topic info">
            </div>
            <button class="btn btn-default" onclick="sendInfo()" type="button">Send to Topic</button>
        </div>
        <div class="col-md-3">
            <div class="form-group">
                <label for="receiver">To</label>
                <select  class="form-control selectpicker"  id="receiver" data-placeholder="select:" >
                    <c:forEach items="${baseUsers}" var="user" varStatus="status">
                        <option value="${user.username}">${user.username}</option>
                    </c:forEach>
                </select>
                <label for="message"></label>
                <input type="text" id="message" class="form-control" placeholder="">
            </div>
            <button  class="btn btn-default" onclick="sendMessage()" type="button">Send to Queue</button>
        </div>
    </div>
    <div id="conversation" class="row">
        <div class="col-md-6">
            <table  class="table table-striped">
                <thead>
                <tr>
                    <th>topic</th>
                </tr>
                </thead>
                <tbody id="topicTable">

                </tbody>
            </table>
        </div>
        <div class="col-md-6">
            <table  class="table table-striped">
                <thead>
                <tr>
                    <th>queue</th>
                </tr>
                </thead>
                <tbody id="queueTable">

                </tbody>
            </table>
        </div>
    </div>
</div>
</body>
</html>

访问系统路径/websocket/index,可看到如下界面

websocket

点击connet 按钮 连接websocket

发送topic 信息

输入topic info信息,点击send to topic 按钮

发送queue mesage

选择接收用户,输入mesage信息,点击send to queue 按钮