An overview of the

In this article, we will introduce the implementation of Spring WebSocket to send messages to specific users. The contents of this article are as follows: 1. First implement a simple login function, which is the necessary condition for sending messages to specific users 2. After logging in to the system, the user can log in to websocket and rewrite MyPrincipal 3. Implement the function of sending messages to specific users. 4. Test

Start by implementing simple login functionality, which is necessary to send messages to specific users

TestMQCtl: The control class provides a mock login to the webSocket page after a successful login

*/ @requestMapping (value ="loginIn", method = RequestMethod.POST)
    public String login(HttpServletRequest request, @RequestParam(required=true) String name, String pwd){ HttpSession httpSession = request.getSession(); // If login succeeds, save to session httpsession.setattribute ("loginName", name);
        return "websocket/sendtouser/ws-sendtouser-rabbitmq"; } /** * go to the login page */ @requestMapping (value =)"login", method = RequestMethod.GET)
    public String loginPage(){// Go to the login pagereturn "websocket/sendtouser/login"; } /** * websocket page * @return
     */
    @RequestMapping(value="/broadcast-rabbitmq/index")
    public String broadcastIndex() {return "websocket/sendtouser/ws-sendtouser-rabbitmq";
    }

Copy the code

Login.jsp a simple form that refers requests to loginIn and goes to the WS-Sendtouser-Rabbitmq. JSP page

<form action="loginIn" method="post"> User name :<inputtype="text" name="name"/> <p> Password :<inputtype="password" name="password" />
    <p>
        <input type="submit" value="submit" />
</form>
Copy the code

Ws-sendtouser-rabbitmq. JSP connects to the WebSocket and subscribs to the message. This JSP was covered in a previous article and is not described in detail here. The page starts webSocket to/WS/ICC/webSocket and subscribes to /user/topic/demo messages

<script type="text/javascript">
    var stompClient = null;

    function setConnected(connected) {
        document.getElementById('connect').disabled = connected;
        document.getElementById('disconnect').disabled = ! connected; document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
        $('#response').html();
    }

    function connect() {/ / websocket connection address, this value is equal to the registry in WebSocketMessageBrokerConfigurer. AddEndpoint ("/ws/icc/websocket"Var socket = new SockJS()'/ws/icc/websocket'); //1
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function(frame) {
            setConnected(true);
            console.log('Connected: '+ frame); // Destination address of the client subscription message: this value is the value specified in the @sendto annotation of BroadcastCtl. stompClient.subscribe('/user/topic/demo'.function(respnose){ showResponse(JSON.parse(respnose.body)); }); }); }function disconnect() {
        if(stompClient ! = null) { stompClient.disconnect(); }setConnected(false);
        console.log("Disconnected");
    }

    function showResponse(message) {
        var response = $("#response");
        response.html(message.name + "<br\>" + response.html());
    }
</script>

Copy the code

After logging in to the system, the user can log in to webSocket and rewrite MyPrincipal

AuthHandshakeInterceptor AuthHandshakeInterceptor is a subclass of HandshakeInterceptor. Check before the Websocket handshake to determine whether the current user is logged in. If you have not logged in, you are not allowed to log in to the Websocket

@Component
public class AuthHandshakeInterceptor implements HandshakeInterceptor {
    private static final Logger log = LoggerFactory.getLogger(AuthHandshakeInterceptor.class);


    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        HttpSession httpSession = getSession(request);
        String user = (String)httpSession.getAttribute("loginName");

        if(StringUtils.isEmpty(user)){
            log.error("Do not log in websocket!");
            return false;
        }
        log.info("login = " + user);

        return true; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, The Exception Exception) {} / / reference HttpSessionHandshakeInterceptor private HttpSession getSession (ServerHttpRequest request) {if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
            return serverRequest.getServletRequest().getSession(false);
        }
        returnnull; }}Copy the code

MyPrincipalHandshakeHandler MyPrincipalHandshakeHandler is a subclass of DefaultHandshakeHandler, handle websocket request, Here we’re just overriding the determineUser method to generate our own Principal, where we’re using loginName to mark the logged-in user instead of the default

@Component
public class MyPrincipalHandshakeHandler extends DefaultHandshakeHandler {
    private static final Logger log = LoggerFactory.getLogger(MyPrincipalHandshakeHandler.class);

    @Override
    protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {

        HttpSession httpSession = getSession(request);
        String user = (String)httpSession.getAttribute("loginName");

        if(StringUtils.isEmpty(user)){
            log.error("Do not log in websocket!");
            return null;
        }
        log.info(" MyDefaultHandshakeHandler login = " + user);
        return new MyPrincipal(user);
    }

    private HttpSession getSession(ServerHttpRequest request) {
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
            return serverRequest.getServletRequest().getSession(false);
        }
        returnnull; }}Copy the code

MyPrincipal defines its own Principal

public class MyPrincipal implements Principal {
    private String loginName;

    public MyPrincipal(String loginName){
        this.loginName = loginName;
    }
    @Override
    public String getName() {
        returnloginName; }}Copy the code

Configuration websocket will we MyPrincipalHandshakeHandler and AuthHandshakeInterceptor in registerStompEndpoints configuration to the service The configureMessageBroker method configures rabbitMQ information, which is omitted here

@configuration // This annotation uses THE STOMP protocol to transport messages based on the message broker, At this point you can @ Controller class using the @ MessageMapping @ EnableWebSocketMessageBroker public class WebSocketRabbitMQMessageBrokerConfigurer extends AbstractWebSocketMessageBrokerConfigurer { @Autowired private MyPrincipalHandshakeHandler myDefaultHandshakeHandler; @Autowired private AuthHandshakeInterceptor sessionAuthHandshakeInterceptor; @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws/icc/websocket") .addInterceptors(sessionAuthHandshakeInterceptor) .setHandshakeHandler(myDefaultHandshakeHandler) .withSockJS(); }... .}Copy the code

Implements the ability to send messages to specific users

We use the convertAndSendToUser of the SimpMessagingTemplate object to send a message to the specified user’s /topic/demo

@Autowired private SimpMessagingTemplate template; /** * @requestMapping (value ="send")
    public String sendMq2UserPage(String msg, String userName){
        return "websocket/sendtouser/send"; } @requestMapping (value =)"send2user")
    @ResponseBody
    public int sendMq2User(String msg, String name){
        System.out.println("= = = = = = = = = = =" + msg + "= = = = = = =" + name);
        RequestMessage demoMQ = new RequestMessage();
        demoMQ.setName(msg);
        template.convertAndSendToUser(name, "/topic/demo", JSON.toJSONString(demoMQ));
        return 0;
    }

Copy the code

Send.jsp simulates the send page

<form action="login" method="post"> Receiver user :<inputtype="text" id="name" name="name" value="<%=session.getAttribute("loginName") % >"/> <p> Message content :<inputtype="text" id="msg" name="msg" />
    <p>
        <input type="button" id="send" value="Send" />
</form>


<script src="/websocket/jquery.js"></script>
<script type=text/javascript>

    $("#send").click(function(){
        $.post("send2user",
            {
                name: $('#name').val(),
                msg: $('#msg').val()
            },
            function(data, status){
                alert("Data: " + data + "\nStatus: " + status);
            });
    });
</script>

Copy the code

test

Test a:

Log on to http://127.0.0.1:8080/ws/login, the use of xiaoming login, and submit

Click Connect. If the connection turns gray, the webSocket login succeeds

Simulation send login page, http://127.0.0.1:8080/ws/send, send xiaoming test – MSG

The page then receives a message:

Test 2:

Open two different browsers and log in to the system as Xiaoming1 and Xiaoming2 respectively. Use the simulation interface to send messages to Xiaoming1, only Xiaoming1 receives the messages

### Conclusion: We have implemented the ability to send messages to specific users

code

Please use tag V0.23 as much as possible. Do not use master because master changes all the time. There is no guarantee that the code in this article will always be the same as the code on Github