Wechat official account: an excellent invalid. If you have any questions, please leave a message in the background. I’m not listening anyway.

preface

Yesterday’s article introduced WebSocket broadcasting, which means that when there is a message on the server, it is sent to all browsers connected to the current endpoint. But that doesn’t solve the problem of who sends messages and who receives them. So, write an article today to implement a one-on-one chat room.

SpringBoot implements WebSocket broadcast messages

The preparatory work

  • Spring Boot 2.1.3 RELEASE
  • Spring Security 2.1.3 RELEASE
  • IDEA
  • JDK8

Pom depends on

Since chat rooms are user-related, the Spring Security 2.1.3 RELEASE dependency is introduced based on the previous article

<! -- Spring Security dependencies -->
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-security</artifactId>
</dependency>
Copy the code

Spring Security configuration

Although Spring Security is involved, due to limited space, this section will only cover the relevant parts of the project. A Spring Security tutorial will be available later.

The configuration of Spring Security is simple, including setting the login path, setting up the Security resources, and creating a user and password in memory. The password needs to be encrypted, and the BCrypt encryption algorithm is used to encrypt the password when the user logs in. The code comments are more detailed than that.

package com.nasus.websocket.config;

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.bcrypt.BCryptPasswordEncoder;

@Configuration
// Enable Spring Security
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
             // Set SpringSecurity not to intercept the/and "/login" paths
            .mvcMatchers("/"."/login").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            // Set the Spring Security login page access path to /login
            .loginPage("/login")
            // Switch to /chat after successful login
            .defaultSuccessUrl("/chat")
            .permitAll()
            .and()
            .logout()
            .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            // Allocate two users nasus and chenzy in memory with the same username and password
            // BCryptPasswordEncoder() is a new encryption method in Spring Security 5.0
            // BCrypt is used to process the user password when logging in.
            .passwordEncoder(new BCryptPasswordEncoder())
            .withUser("nasus")
            // Ensure that the user logs in using bcrypt to process the password and compare it with the password in memory
            .password(new BCryptPasswordEncoder().encode("nasus")).roles("USER")
            .and()
            // BCrypt is used to process the user password when logging in.
            .passwordEncoder(new BCryptPasswordEncoder())
            .withUser("chenzy")
            // Ensure that the user logs in using bcrypt to process the password and compare it with the password in memory
            .password(new BCryptPasswordEncoder().encode("chenzy")).roles("USER");
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // /resource/static directory static resources, Spring Security does not intercept
        web.ignoring().antMatchers("/resource/static**"); }}Copy the code

WebSocket configuration

Register a node named “/endpointChat” on the basis of the previous article for users to subscribe, only users who subscribe to this node can receive messages; Then, add a message broker named “/queue”.

@Configuration
/ / @ EnableWebSocketMessageBroker annotations for open use STOMP protocol to transmit messages based on agent (MessageBroker), then the controller (the controller)
// Start supporting @messagemapping like @requestMapping.
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {


    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // Register a Stomp node (endpoint) named /endpointNasus and specify the use of the SockJS protocol.
        registry.addEndpoint("/endpointNasus").withSockJS();
        // Register a Stomp node (endpoint) named /endpointChat and specify the SockJS protocol.
        registry.addEndpoint("/endpointChat").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // The broadcast configuration is called /nasus message broker, which must match or match the address prefix configured by @sendto in controller
        // Add a point-to-point /queue message broker
        registry.enableSimpleBroker("/queue"."/nasus/getResponse"); }}Copy the code

Controller is the controller

Specify the format and template for sending messages. See code comments for details.

@Autowired
// Use SimpMessagingTemplate to send information to the browser
private SimpMessagingTemplate messagingTemplate;

@MessageMapping("/chat")
public void handleChat(Principal principal,String msg){
    // In SpringMVC, you can get the Principal directly in the argument, which contains the current user information
    if (principal.getName().equals("nasus")) {// Hardcode if the sender is Nasus and the receiver is chenzy and vice versa.
        / / by messageingTemplate convertAndSendToUser method to send information to the user, parameters it is receiving messages of users, parameters of the second is the browser subscription address, parameters of the three is the message itself
        messagingTemplate.convertAndSendToUser("chenzy"."/queue/notifications",principal.getName()+"-send:" + msg);
    } else {
        messagingTemplate.convertAndSendToUser("nasus"."/queue/notifications",principal.getName()+"-send:"+ msg); }}Copy the code

The login page

<! 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 > landing page < / title > < / head > < body > < div th:if="${param.error}"> invalid account and password </div> <div th:if="${param.logout}"> you logged off </div> <form th:action="@{/login}" method="post">< div><label> Account: <inputtype="text" name="username"/> </label></div> <div><labeltype="password" name="password"/> </label></div>
    <div><input type="submit" value="Login"/></div>
</form>
</body>
</html>
Copy the code

The chat page

<! 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> Chat room </p> <form id="nasusForm">
    <textarea rows="4" cols="60" name="text"></textarea>
    <input type="submit"/>
</form>

<script th:inline="javascript">
    $('#nasusForm').submit(function(e){
        e.preventDefault();
        var text = $('#nasusForm').find('textarea[name="text"]').val(); sendSpittle(text); }); // The endpoint name to connect to SockJs is"/endpointChat"
    var sock = new SockJS("/endpointChat");
    var stomp = Stomp.over(sock);
    stomp.connect('guest'.'guest'.function(frame) {/ / subscribe/user/queue/notifications to send messages, Here and in the controller / / messagingTemplate convertAndSendToUser subscription address in much more consistent / / here/user prefix, is a must, Subscribe (stomp. Subscribe)"/user/queue/notifications", handleNotification);
    });



    function handleNotification(message) {
        $('#output').append("<b>Received: " + message.body + "</b><br/>")}function sendSpittle(text) {
        stomp.send("/chat", {}, text);
    }
    $('#stop').click(function() {sock.close()});
</script>

<div id="output"></div>
</body>
</html>
Copy the code

Page Controller

@Controller
public class ViewController {

    @GetMapping("/nasus")
    public String getView() {return "nasus";
    }

    @GetMapping("/login")
    public String getLoginView() {return "login";
    }

    @GetMapping("/chat")
    public String getChatView() {return "chat"; }}Copy the code

test

The expected result is that two users log in to the system and can send messages to each other. However, the sessions of users of the same browser are shared, so you need to add another user to Chrome.

For details, go to Settings > Manage Users > Add Users in Chrome.

Two users visit http://localhost:8080/login login system respectively, jump to the chat interface:

To send messages to each other:

The complete code

https://github.com/turoDog/Demo/tree/master/springboot_websocket_demo

If you think it is helpful, please give a Star and then go. Thank you very much.

After the language

If this article is of any help to you, please help to read it. Your good looks are my motivation to keep writing.

In addition, after paying attention to send 1024 can receive free learning materials.

For more information, see this article: Python, C++, Java, Linux, Go, front-end, algorithm sharing