Spring Boot and Spring Security integration WebSocket examples

One. Why WebSocket
in the HTTP protocol, all requests are initiated by the client, the server will respond, the server can not push message to the client, but in need of some instant messaging applications, there is an inevitable need of services to end push message client, the conventional solutions there are several
polling
polling is the most simple solution, in the sense that the client sends a request to keep the service side at a fixed time interval, the service side view is there new data, if the server has new data is returned to the client, the server if there is no new data, an empty JSON or XML document is returned, easy to implement polling for developers, but the drawbacks obvious: each client must create a new HTTP request to the server to handle a large number of invalid requests, in a highly concurrent scenarios will seriously slow down the efficiency of the server, but the server resources are greatly wasted, therefore, such undesirable manner and
2. long polling
long polling polling for the existence of a partial solution to the problem do, in the long polling, receiving at the server It will respond to the request when the client does not respond immediately to the client, but will wait until the server has new data request from the client immediately, otherwise the server will hold this request without return until the new data only return, this way to some extent, saves the resources of the server, but there are some problems, such as:
(1) if the browser has new data to be sent before the service response can only create a new concurrent requests, or try to cut off the current request, in creating a new request
(2) TCP and HTTP connection timeout specifications have a say, so-called long polling continues and can not connect the server side and client needs regular and close connection , which increases the workload of developers, technology can extend the connection time, but this is not a mainstream solution
3.Applet and Flash (coming off the shelf)
II. About WebSocket
WebSocket protocol is a full-duplex communication over a single TCP connection, as has been W3C standards, such use may WebSocket data exchange between the server and the client easier, it allows the server to the client actively push the data, in the WebSocket protocol, the browser and the server only needs to complete a handshake, you can create a persistent connection between the two, and two-way data transmission
WebSocket protocol using the upgrade feature of HTTP / 1.1, a WebSocket request is first using non-normal HTTP request to access a URL in a particular pattern, the URL has two modes, respectively, and ws is the WSS, the corresponding HTTP protocol HTTP and the HTTPS, a Connection request header: Upgrade field indicating the client wants to upgrade to the agreement, there is another upgrade: websocket field, the request indicates that the client wants to upgrade to the WebSocket protocol agreement, the two fields together to tell the server you are upgrading WebSocket connection to such a full-duplex protocol, if the server consent agreement upgrade, then after handshake, text message or other Binary messages can be simultaneously sent in both directions, without the need to shut down and re-connect, at this time the client may end the service relationship is such that they can each send a message to the other party take the initiative, and traditional solutions compared embodiment, WebSocket has the following characteristics:
(1) create a need to use WebSocket connection, which makes WebSocket be a stateful protocol, the process after the passage part state information (e.g. authentication, etc.) can be omitted
(2 ) WebSocket connection on port 80 (ws) or 443 (wss) connected to the same port used for HTTP, so that substantially all of the firewall will not block the connection of WebSocket
(. 3) using the HTTP protocol WebSocket handshake, he is possible to directly integrate to the web browser and the HTTP server, no extra costs
(4) a heartbeat message (ping and Pong) is repeatedly pushed, in the active state to maintain a consistent WebSocket
(5) The protocol started or when a message arrives, the server and the client can know
(6) Websocket sends a special message to close off the connection
(7) WebSocket support cross-domain, to avoid restriction Ajax
( 8) HTTP specification requires the browser will connect two concurrent connections limit for each host name, but when we use WebSocket, and when the handshake is completed, the limit does not exist, because the connection is no longer the HTTP the connector
(9) WebSocket support extended protocols, the user can extend the protocol to implement a custom sub-protocol part
(10) binary better support and better compression
three. Spring Boot integrate the WebSocket
1. sending message
to create a project: First, create a Spring Boot project, add the following dependence

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.webjars/webjars-locator-core -->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>webjars-locator-core</artifactId>
            <version>0.35</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.webjars/sockjs-client -->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>sockjs-client</artifactId>
            <version>1.1.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.webjars/stomp-websocket -->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>stomp-websocket</artifactId>
            <version>2.3.3-1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.webjars/jquery -->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.3.1-1</version>
        </dependency>

WebSocket Spring-boot-starter-websocket time-dependent correlation dependence, others are front-end library, use the jar package in the form of unified management of these front-end library, use webjsr added to the front of the library project has been the default in Spring Boot project added 'plus static resources, can be used directly
configuration WebSocket
protocol based Spring framework provides support of WebSocket STOMP, STOMP is a simple interactive operations, typically used between the client asynchronously through intermediate server messaging, WebSocket configured as follows:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");
        registry.setApplicationDestinationPrefixes("/app");
    }
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/chat").withSockJS();
    }
}

Code explanation:

(1) Custom WebSocketConfig Inherited from WebSocketMessageBrokerConfigurer for WebSocket configuration and opened by @EnableWebSocketMessageBroker WebSocket message annotation proxy
(2) registry.enableSimpleBroker ( "/ topic ") prefix indicates the message broker is provided, i.e., if the prefix of the message is "/ topic ", will forward the message to the proxy (Broker), and then the broadcast message by the message broker to the client in the current connection
(3) registry.setApplicationDestinationPrefixes (" / app ") shows an arrangement of one or more prefixes, these prefixes filtering out the message needs to be processed annotated method
(4) registry.addEndpoint ( "/ chat "). withSockJS () represents the definition of a edPoint prefix "/ chat" and open sockjs support, sockjs can solve the browser WebSocket compatibility, the URL where the client configured to establish WebSocket connection
3. Controller definitions
defined Controller used to implement processing of the message

@RestController
public class GreetingController {

    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Message greeting(Message message)throws Exception{
        return message;     
    }
}

According to a second configuration portions, the method @ MessageMapping ( "/ hello") for receiving the annotation message "/ app / hello" path transmitted, after processing the message, and then forwards the message to the annotation process @ SendTo defined path, the path is a @SendTo the path "/ topic", so the message will be left to the message Broker agent, and then broadcast by Broker
4. front page

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>单聊</title>
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
<script src="/chat.js"></script>
</head><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>群聊</title>
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
<script src="/app.js"></script>
</head>
<body>
    <div>
        <label for="name">请输入用户名:</label> 
        <input type="text" id="name" placeholder="用户名">
    </div>
    <div>
        <button id="connect" type="button">连接</button>
        <button id="disconnect" type="button" disabled="disabled">断开连接</button>
    </div>
    <div id="chat" style="display: none;">
        <div>
            <label for="name">请输入聊天内容:</label> 
            <input type="text" id="content" placeholder="聊天内容">
        </div>
        <button id="send" type="button">发送</button>
        <div id="greetings">
            <div id="conversation" style="display: none">群聊进行中...</div>
        </div>
    </div>
</body>
</html>
<body>
    <div id="chat">
        <div id="chatsContent"></div>
        <div>
            请输入聊天内容: <input type="text" id="content" placeholder="聊天内容">
            目标用户: <input type="text" id="to" placeholder="目标用户">
            <button id="send" type="button">发送</button>
        </div>
    </div>
</body>
</html>

5. js logical pages and the websocket

var stompClient = null;
function setConnected(connected) {
    $("#connect").prop("disabled", connected);
    $("#disconnect").prop("disabled", !connected);
    if (connected) {
        $("#conversation").show();
        $("#chat").show();
    } else {
        $("#conversation").hide();
        $("#chat").hide();
    }
    $("#greetings").html("");
}
function connect() {
    if (!$("#name").val()) {
        return;
    }
    var socket = new SockJS('/chat');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function(frame) {
        setConnected(true);
        stompClient.subscribe('/topic/greetings', function(greeting) {
            showGreeting(JSON.parse(greeting.body));
        });
    });
}
function disconnect() {
    if (stompClient !== null) {
        stompClient.disconnect();
    }
    setConnected(false);
}
function sendName() {
    stompClient.send("/app/hello", {}, JSON.stringify({
        'name' : $("#name").val(),
        'content' : $("#content").val()
    }));
}
function showGreeting(message) {
    $("#greetings").append(
            "<div>" + message.name + ":" + message.content + "</div>");
}

$(function() {
    $("#connect").click(function() {
        connect();
    });
    $("#disconnect").click(function() {
        disconnect();
    });
    $("#send").click(function() {
        sendName();
    });
});

Code explanation:
(. 1) Connect method represents establish a WebSocket connection, when establishing WebSocket connection, the user must enter a user name, and then to establish a connection
(2) Method body means: using SockJS establish a connection, and then create a STOMP instance initiate a request, the connection is successful callback method, the first call setConnected (true); method set up the page, and then call the subscribe method STOPM the subscription service sends messages back and show it to the message sent by the server (using showGreeting method)
the disconnect process (3) invoked STOMP can disconnect a connection WebScoket
5. entity class

public class Message {
    private String name;
    private String content;
........getter,setter..............
}

6. reform message Controller
messages sent using the @SendTo to comment, the comment speaks treated with the method of the message is forwarded to the broker, and then broadcast by the broker, in addition to @SendTo annotations, Spring also provides SimpMessagingTemplate classes to allow developers to more flexible sending a message, can be modified for use SimpMessagingTemlate above Controller, the transformation results are as follows:

@RestController
public class GreetingController {
    @Autowired
    SimpMessagingTemplate simpMessagingTemplate;
    @MessageMapping("/hello")
    public void greeting(Message message)throws Exception{
        simpMessagingTemplate.convertAndSend("/topic/greetings",message);
    }
}

four. Send Message Peer to Peer
1. Add dependency
since it is a point to point transmission, there should be the user's concept, and therefore depends first added Spring Security in the project, as follows:

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

Configuring Spring Security
to configure spring security, add two users, configure all addresses are after authentication to access the following code:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    /**
     * 密码加密过:123
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
        .withUser("齐**")
        .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
        .roles("admin")
        .and()
        .withUser("辛**")
        .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
        .roles("user")
        .and()
        .withUser("李**")
        .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
        .roles("user")
        .and()
        .withUser("岳**")
        .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
        .roles("user")
        .and()
        .withUser("尚**")
        .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
        .roles("user");     
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
        .anyRequest()
        .authenticated()
        .and()
        .formLogin()
        .permitAll();
    }
}

3. transformation WebSocket configuration code is as follows:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic","/queue");
        registry.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/chat").withSockJS();
    }
}

Code explanation:
Here the modified registry.enableSimpleBroker ( "/ topic"), added prefix "/ queue", facilitate peer group message and message management
4. Controller configuration
of the Controller WebSocket transform, as follows:

@RestController
public class GreetingController {

    @Autowired
    SimpMessagingTemplate simpMessagingTemplate;

    /**
     * 消息群发
     * @param message
     * @return
     * @throws Exception
     */
    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Message greeting(Message message) throws Exception {
        return message;

    }

    /**
     * 点对点发送
     * @param principal
     * @param chat
     * @throws Exception
     */
    @MessageMapping("/chat")
    public void chat(Principal principal, Chat chat) throws Exception {
        String from=principal.getName();
        chat.setFrom(from);
        simpMessagingTemplate.convertAndSendToUser(chat.getTo(), "/queue/chat", chat);;
    }
}

Code explanation:

(1) still use @SendTo group message is achieved, the point is achieved with SimpMessagingTemplate

(2) @MessageMapping ( "/ chat") indicates that the message from the "/ app / chat" chat processing path will be the method, the first parameter may be the Principal chat method used to obtain information about the current logged-on user, the second parameter message is sent by the client

(3) in the chat method, first get the current logged-on user's user name, set the properties from the object to chat, and then send out the message, the target is sent to the chat properties

Method (4) message using convertAndSendToUser, the interior of the method calls convertAndSend method, and the processing done message path

The message entity classes:

public class Chat {
    private String to;
    private String from;
    private String content;
    ........getter,setter省略............
}

Code explanation:
Chat is an ordinary javaBean, to attribute indicates the target user messages, form indicates that the message came from, content is the main content of the message
6. the front page - Chat page (created in onlinechat.html)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>单聊</title>
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
<script src="/chat.js"></script>
</head>
<body>
    <div id="chat">
        <div id="chatsContent"></div>
        <div>
            请输入聊天内容: <input type="text" id="content" placeholder="聊天内容">
            目标用户: <input type="text" id="to" placeholder="目标用户">
            <button id="send" type="button">发送</button>
        </div>
    </div>
</body>
</html>

5. js logical pages and the websocket

var stompClient = null;
function setConnected(connected) {
    $("#connect").prop("disabled", connected);
    $("#disconnect").prop("disabled", !connected);
    if (connected) {
        $("#conversation").show();
        $("#chat").show();
    } else {
        $("#conversation").hide();
        $("#chat").hide();
    }
    $("#greetings").html("");
}
function connect() {
    if (!$("#name").val()) {
        return;
    }
    var socket = new SockJS('/chat');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function(frame) {
        setConnected(true);
        stompClient.subscribe('/topic/greetings', function(greeting) {
            showGreeting(JSON.parse(greeting.body));
        });
    });
}
function disconnect() {
    if (stompClient !== null) {
        stompClient.disconnect();
    }
    setConnected(false);
}
function sendName() {
    stompClient.send("/app/hello", {}, JSON.stringify({
        'name' : $("#name").val(),
        'content' : $("#content").val()
    }));
}
function showGreeting(message) {
    $("#greetings").append(
            "<div>" + message.name + ":" + message.content + "</div>");
}

$(function() {
    $("#connect").click(function() {
        connect();
    });
    $("#disconnect").click(function() {
        disconnect();
    });
    $("#send").click(function() {
        sendName();
    });
});

Code explanation:
(1) connection is successful, the subscriber address "/ user / queue / chat" , the address more "/ user" prefix than the address of the server configuration, because, SimpMessagingTemplate class automatically adds path prefix
(2) transmits a chat message path "/ App / chat"
(. 3) the message content sent to a field which is used to describe the target message sent by user

Guess you like

Origin blog.51cto.com/13501268/2403079